datamapper 0.1.0 → 0.1.1

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