datamapper 0.1.0 → 0.1.1

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.
Files changed (73) hide show
  1. data/CHANGELOG +31 -1
  2. data/MIT-LICENSE +1 -1
  3. data/README +9 -1
  4. data/example.rb +23 -15
  5. data/lib/data_mapper.rb +5 -0
  6. data/lib/data_mapper/adapters/abstract_adapter.rb +9 -207
  7. data/lib/data_mapper/adapters/mysql_adapter.rb +132 -108
  8. data/lib/data_mapper/adapters/postgresql_adapter.rb +242 -0
  9. data/lib/data_mapper/adapters/sql/coersion.rb +74 -0
  10. data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +140 -0
  11. data/lib/data_mapper/adapters/sql/commands/conditions.rb +161 -0
  12. data/lib/data_mapper/adapters/sql/commands/delete_command.rb +113 -0
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +296 -0
  14. data/lib/data_mapper/adapters/sql/commands/save_command.rb +141 -0
  15. data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +33 -0
  16. data/lib/data_mapper/adapters/sql/mappings/column.rb +91 -0
  17. data/lib/data_mapper/adapters/sql/mappings/schema.rb +30 -0
  18. data/lib/data_mapper/adapters/sql/mappings/table.rb +143 -0
  19. data/lib/data_mapper/adapters/sql/quoting.rb +38 -0
  20. data/lib/data_mapper/adapters/sql_adapter.rb +163 -0
  21. data/lib/data_mapper/adapters/sqlite3_adapter.rb +155 -116
  22. data/lib/data_mapper/associations.rb +2 -0
  23. data/lib/data_mapper/associations/advanced_has_many_association.rb +55 -0
  24. data/lib/data_mapper/associations/belongs_to_association.rb +2 -2
  25. data/lib/data_mapper/associations/has_many_association.rb +3 -3
  26. data/lib/data_mapper/associations/has_one_association.rb +2 -2
  27. data/lib/data_mapper/base.rb +30 -11
  28. data/lib/data_mapper/callbacks.rb +4 -1
  29. data/lib/data_mapper/database.rb +8 -41
  30. data/lib/data_mapper/identity_map.rb +23 -3
  31. data/lib/data_mapper/session.rb +34 -186
  32. data/lib/data_mapper/{extensions → support}/active_record_impersonation.rb +16 -12
  33. data/lib/data_mapper/support/blank.rb +35 -0
  34. data/lib/data_mapper/support/connection_pool.rb +2 -1
  35. data/lib/data_mapper/support/string.rb +27 -0
  36. data/lib/data_mapper/support/struct.rb +26 -0
  37. data/lib/data_mapper/validations/unique_validator.rb +1 -3
  38. data/lib/data_mapper/validations/validation_helper.rb +1 -1
  39. data/performance.rb +24 -7
  40. data/profile_data_mapper.rb +24 -2
  41. data/rakefile.rb +2 -2
  42. data/spec/basic_finder.rb +2 -2
  43. data/spec/belongs_to.rb +1 -1
  44. data/spec/delete_command_spec.rb +9 -0
  45. data/spec/fixtures/zoos.yaml +4 -0
  46. data/spec/has_many.rb +1 -1
  47. data/spec/load_command_spec.rb +44 -0
  48. data/spec/models/zoo.rb +2 -0
  49. data/spec/save_command_spec.rb +13 -0
  50. data/spec/spec_helper.rb +10 -1
  51. data/spec/support/string_spec.rb +7 -0
  52. data/spec/validates_confirmation_of.rb +1 -1
  53. data/spec/validates_format_of.rb +1 -1
  54. data/spec/validates_length_of.rb +1 -1
  55. data/spec/validations.rb +1 -1
  56. metadata +23 -20
  57. data/lib/data_mapper/extensions/callback_helpers.rb +0 -35
  58. data/lib/data_mapper/loaded_set.rb +0 -45
  59. data/lib/data_mapper/mappings/column.rb +0 -78
  60. data/lib/data_mapper/mappings/schema.rb +0 -28
  61. data/lib/data_mapper/mappings/table.rb +0 -99
  62. data/lib/data_mapper/queries/conditions.rb +0 -141
  63. data/lib/data_mapper/queries/connection.rb +0 -34
  64. data/lib/data_mapper/queries/create_table_statement.rb +0 -38
  65. data/lib/data_mapper/queries/delete_statement.rb +0 -17
  66. data/lib/data_mapper/queries/drop_table_statement.rb +0 -17
  67. data/lib/data_mapper/queries/insert_statement.rb +0 -29
  68. data/lib/data_mapper/queries/reader.rb +0 -42
  69. data/lib/data_mapper/queries/result.rb +0 -19
  70. data/lib/data_mapper/queries/select_statement.rb +0 -103
  71. data/lib/data_mapper/queries/table_exists_statement.rb +0 -17
  72. data/lib/data_mapper/queries/truncate_table_statement.rb +0 -17
  73. data/lib/data_mapper/queries/update_statement.rb +0 -25
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
- module Extensions
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.find(self.class, id, :select => session.mappings[self.class].columns.map(&:name), :reload => true)
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
- find(:all, options, &b)
33
+ database.all(self, options, &b)
30
34
  end
31
35
 
32
- def first(options = {}, &b)
33
- find(:first, options, &b)
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(*args, &b)
45
- DataMapper::database.find(self, *args, &b)
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
- if id_or_hash.kind_of?(Hash)
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
@@ -1,4 +1,5 @@
1
- require 'thread'
1
+ # See 'fastthread' dependency in data_mapper.rb
2
+ # require 'thread'
2
3
 
3
4
  module DataMapper
4
5
  module Support
@@ -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
- # HACK: gotta make sure we're using the same database that this instance was
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
@@ -11,7 +11,7 @@ end.each do |validator|
11
11
  end
12
12
 
13
13
  module DataMapper
14
- module Extensions
14
+ module Validations
15
15
 
16
16
  module ValidationHelper
17
17
 
data/performance.rb CHANGED
@@ -14,8 +14,7 @@ class ARPerson < ActiveRecord::Base
14
14
  set_table_name 'people'
15
15
  end
16
16
 
17
- $LOAD_PATH.unshift('lib')
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
- log_stream 'development.log'
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 = ?", 'elephant'])
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 => 'elephant')
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
@@ -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[:name => 'Galveston']
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/*.rb']
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.0'
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.find(Animal, :first, :conditions => ['name = ?', 'Frog'])
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.find(Animal, :first).name.should == 'Frog'
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
@@ -11,7 +11,7 @@ context 'An Exhibit' do
11
11
 
12
12
  specify 'belongs to a zoo' do
13
13
  database do |db|
14
- @aviary.zoo.should == @aviary.session.find(Zoo, :first, :name => 'San Diego')
14
+ @aviary.zoo.should == @aviary.session.first(Zoo, :name => 'San Diego')
15
15
  end
16
16
  end
17
17
 
@@ -0,0 +1,9 @@
1
+ describe DataMapper::Adapters::Sql::Commands::DeleteCommand do
2
+
3
+ it "should drop and create the table" do
4
+ database.schema[Zoo].drop!.should == true
5
+ database.schema[Zoo].exists?.should == false
6
+ database.schema[Zoo].create!.should == true
7
+ end
8
+
9
+ end
@@ -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.find(Exhibit, :first, :name => 'Monkey Mayhem'))
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'
data/spec/models/zoo.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  class Zoo < DataMapper::Base
2
2
  property :name, :string
3
+ property :notes, :text
3
4
 
4
5
  has_many :exhibits
6
+ advanced_has_many :exhibits2, :class_name => 'Exhibit', :foreign_key => 'cow_id'
5
7
  end