datamapper 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/CHANGELOG +65 -0
  2. data/README +193 -1
  3. data/do_performance.rb +153 -0
  4. data/environment.rb +45 -0
  5. data/example.rb +119 -22
  6. data/lib/data_mapper.rb +36 -16
  7. data/lib/data_mapper/adapters/abstract_adapter.rb +8 -0
  8. data/lib/data_mapper/adapters/data_object_adapter.rb +360 -0
  9. data/lib/data_mapper/adapters/mysql_adapter.rb +30 -179
  10. data/lib/data_mapper/adapters/postgresql_adapter.rb +90 -199
  11. data/lib/data_mapper/adapters/sql/coersion.rb +32 -3
  12. data/lib/data_mapper/adapters/sql/commands/conditions.rb +97 -128
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +234 -231
  14. data/lib/data_mapper/adapters/sql/commands/loader.rb +99 -0
  15. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +30 -0
  16. data/lib/data_mapper/adapters/sql/mappings/column.rb +68 -6
  17. data/lib/data_mapper/adapters/sql/mappings/schema.rb +6 -3
  18. data/lib/data_mapper/adapters/sql/mappings/table.rb +71 -42
  19. data/lib/data_mapper/adapters/sql/quoting.rb +8 -2
  20. data/lib/data_mapper/adapters/sqlite3_adapter.rb +32 -201
  21. data/lib/data_mapper/associations.rb +21 -7
  22. data/lib/data_mapper/associations/belongs_to_association.rb +96 -80
  23. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +158 -67
  24. data/lib/data_mapper/associations/has_many_association.rb +96 -78
  25. data/lib/data_mapper/associations/has_n_association.rb +64 -0
  26. data/lib/data_mapper/associations/has_one_association.rb +49 -79
  27. data/lib/data_mapper/associations/reference.rb +47 -0
  28. data/lib/data_mapper/base.rb +216 -50
  29. data/lib/data_mapper/callbacks.rb +71 -24
  30. data/lib/data_mapper/{session.rb → context.rb} +20 -8
  31. data/lib/data_mapper/database.rb +176 -45
  32. data/lib/data_mapper/embedded_value.rb +65 -0
  33. data/lib/data_mapper/identity_map.rb +12 -4
  34. data/lib/data_mapper/support/active_record_impersonation.rb +12 -8
  35. data/lib/data_mapper/support/enumerable.rb +8 -0
  36. data/lib/data_mapper/support/serialization.rb +13 -0
  37. data/lib/data_mapper/support/string.rb +1 -12
  38. data/lib/data_mapper/support/symbol.rb +3 -0
  39. data/lib/data_mapper/validations/unique_validator.rb +1 -2
  40. data/lib/data_mapper/validations/validation_helper.rb +18 -1
  41. data/performance.rb +109 -34
  42. data/plugins/can_has_sphinx/LICENSE +23 -0
  43. data/plugins/can_has_sphinx/README +4 -0
  44. data/plugins/can_has_sphinx/REVISION +1 -0
  45. data/plugins/can_has_sphinx/Rakefile +22 -0
  46. data/plugins/can_has_sphinx/init.rb +1 -0
  47. data/plugins/can_has_sphinx/install.rb +1 -0
  48. data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +123 -0
  49. data/plugins/can_has_sphinx/lib/sphinx.rb +460 -0
  50. data/plugins/can_has_sphinx/scripts/sphinx.sh +47 -0
  51. data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +41 -0
  52. data/plugins/dataobjects/REVISION +1 -0
  53. data/plugins/dataobjects/Rakefile +7 -0
  54. data/plugins/dataobjects/do.rb +246 -0
  55. data/plugins/dataobjects/do_mysql.rb +179 -0
  56. data/plugins/dataobjects/do_postgres.rb +181 -0
  57. data/plugins/dataobjects/do_sqlite3.rb +153 -0
  58. data/plugins/dataobjects/spec/do_spec.rb +150 -0
  59. data/plugins/dataobjects/spec/spec_helper.rb +81 -0
  60. data/plugins/dataobjects/swig_mysql/do_mysql.bundle +0 -0
  61. data/plugins/dataobjects/swig_mysql/extconf.rb +33 -0
  62. data/plugins/dataobjects/swig_mysql/mysql_c.c +18800 -0
  63. data/plugins/dataobjects/swig_mysql/mysql_c.i +8 -0
  64. data/plugins/dataobjects/swig_mysql/mysql_supp.i +46 -0
  65. data/plugins/dataobjects/swig_postgres/Makefile +146 -0
  66. data/plugins/dataobjects/swig_postgres/extconf.rb +29 -0
  67. data/plugins/dataobjects/swig_postgres/postgres_c.bundle +0 -0
  68. data/plugins/dataobjects/swig_postgres/postgres_c.c +8185 -0
  69. data/plugins/dataobjects/swig_postgres/postgres_c.i +73 -0
  70. data/plugins/dataobjects/swig_sqlite/db +0 -0
  71. data/plugins/dataobjects/swig_sqlite/extconf.rb +9 -0
  72. data/plugins/dataobjects/swig_sqlite/sqlite3_c.c +4725 -0
  73. data/plugins/dataobjects/swig_sqlite/sqlite_c.i +168 -0
  74. data/rakefile.rb +45 -23
  75. data/spec/acts_as_tree_spec.rb +39 -0
  76. data/spec/associations_spec.rb +220 -0
  77. data/spec/attributes_spec.rb +15 -0
  78. data/spec/base_spec.rb +44 -0
  79. data/spec/callbacks_spec.rb +45 -0
  80. data/spec/can_has_sphinx.rb +6 -0
  81. data/spec/coersion_spec.rb +34 -0
  82. data/spec/conditions_spec.rb +49 -0
  83. data/spec/conversions_to_yaml_spec.rb +17 -0
  84. data/spec/count_command_spec.rb +11 -0
  85. data/spec/delete_command_spec.rb +1 -1
  86. data/spec/embedded_value_spec.rb +23 -0
  87. data/spec/fixtures/animals_exhibits.yaml +2 -0
  88. data/spec/fixtures/people.yaml +18 -1
  89. data/spec/{legacy.rb → legacy_spec.rb} +3 -3
  90. data/spec/load_command_spec.rb +157 -20
  91. data/spec/magic_columns_spec.rb +9 -0
  92. data/spec/mock_adapter.rb +20 -0
  93. data/spec/models/animal.rb +1 -1
  94. data/spec/models/animals_exhibit.rb +6 -0
  95. data/spec/models/exhibit.rb +2 -0
  96. data/spec/models/person.rb +26 -1
  97. data/spec/models/project.rb +19 -0
  98. data/spec/models/sales_person.rb +1 -0
  99. data/spec/models/section.rb +6 -0
  100. data/spec/models/zoo.rb +3 -1
  101. data/spec/query_spec.rb +9 -0
  102. data/spec/save_command_spec.rb +65 -1
  103. data/spec/schema_spec.rb +89 -0
  104. data/spec/single_table_inheritance_spec.rb +27 -0
  105. data/spec/spec_helper.rb +9 -55
  106. data/spec/{symbolic_operators.rb → symbolic_operators_spec.rb} +9 -5
  107. data/spec/{validates_confirmation_of.rb → validates_confirmation_of_spec.rb} +4 -3
  108. data/spec/{validates_format_of.rb → validates_format_of_spec.rb} +5 -4
  109. data/spec/{validates_length_of.rb → validates_length_of_spec.rb} +8 -7
  110. data/spec/{validates_uniqueness_of.rb → validates_uniqueness_of_spec.rb} +7 -10
  111. data/spec/{validations.rb → validations_spec.rb} +24 -6
  112. data/tasks/drivers.rb +20 -0
  113. data/tasks/fixtures.rb +42 -0
  114. metadata +181 -42
  115. data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +0 -140
  116. data/lib/data_mapper/adapters/sql/commands/delete_command.rb +0 -113
  117. data/lib/data_mapper/adapters/sql/commands/save_command.rb +0 -141
  118. data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +0 -33
  119. data/lib/data_mapper/adapters/sql_adapter.rb +0 -163
  120. data/lib/data_mapper/associations/advanced_has_many_association.rb +0 -55
  121. data/lib/data_mapper/support/blank_slate.rb +0 -3
  122. data/lib/data_mapper/support/proc.rb +0 -69
  123. data/lib/data_mapper/support/struct.rb +0 -26
  124. data/lib/data_mapper/unit_of_work.rb +0 -38
  125. data/spec/basic_finder.rb +0 -67
  126. data/spec/belongs_to.rb +0 -47
  127. data/spec/has_and_belongs_to_many.rb +0 -25
  128. data/spec/has_many.rb +0 -34
  129. data/spec/new_record.rb +0 -24
  130. data/spec/sub_select.rb +0 -16
  131. data/spec/support/string_spec.rb +0 -7
@@ -0,0 +1,9 @@
1
+ describe "Magic Columns" do
2
+
3
+ it "should update updated_at on save" do
4
+ zoo = Zoo.new(:name => 'Mary')
5
+ zoo.save
6
+ zoo.updated_at.should be_a_kind_of(Time)
7
+ end
8
+
9
+ end
@@ -0,0 +1,20 @@
1
+ require 'data_mapper/adapters/data_object_adapter'
2
+
3
+ class MockAdapter < DataMapper::Adapters::DataObjectAdapter
4
+ COLUMN_QUOTING_CHARACTER = "`"
5
+ TABLE_QUOTING_CHARACTER = "`"
6
+
7
+ def delete(instance_or_klass, options = nil)
8
+ end
9
+
10
+ def save(session, instance)
11
+ end
12
+
13
+ def load(session, klass, options)
14
+ end
15
+
16
+ def table_exists?(name)
17
+ true
18
+ end
19
+
20
+ end
@@ -1,6 +1,6 @@
1
1
  class Animal < DataMapper::Base
2
2
  property :name, :string
3
- property :notes, :text, :lazy => true
3
+ property :notes, :text
4
4
 
5
5
  has_one :favourite_fruit, :class => 'Fruit', :foreign_key => 'devourer_id'
6
6
  has_and_belongs_to_many :exhibits
@@ -0,0 +1,6 @@
1
+ # This is just here to get around the fact I use a Class to load
2
+ # fixtures right now.
3
+ class AnimalsExhibit < DataMapper::Base
4
+ property :animal_id, :integer, :key => true, :serial => false
5
+ property :exhibit_id, :integer
6
+ end
@@ -1,6 +1,8 @@
1
1
  class Exhibit < DataMapper::Base
2
2
  property :name, :string
3
3
 
4
+ validates_presence_of :name
5
+
4
6
  belongs_to :zoo
5
7
  has_and_belongs_to_many :animals
6
8
  end
@@ -3,5 +3,30 @@ class Person < DataMapper::Base
3
3
  property :age, :integer
4
4
  property :occupation, :string
5
5
  property :type, :class
6
- property :notes, :text, :lazy => true
6
+ property :notes, :text
7
+ property :date_of_birth, :datetime
8
+
9
+ embed :address do
10
+ property :street, :string
11
+ property :city, :string
12
+ property :state, :string, :size => 2
13
+ property :zip_code, :string, :size => 10
14
+
15
+ def city_state_zip_code
16
+ "#{city}, #{state} #{zip_code}"
17
+ end
18
+
19
+ end
20
+
21
+ class Location < DataMapper::EmbeddedValue
22
+ property :city, :string
23
+ property :state, :string, :size => 2
24
+
25
+ def to_s
26
+ "#{city}, #{state}"
27
+ end
28
+ end
29
+
30
+ embed Location
31
+
7
32
  end
@@ -0,0 +1,19 @@
1
+ class Project < DataMapper::Base
2
+ property :title, :string
3
+ property :description, :string
4
+
5
+ has_many :sections
6
+
7
+ before_create :create_main_section
8
+
9
+ def tickets
10
+ return [] if sections.empty?
11
+ sections.map { |section| section.tickets }
12
+ end
13
+
14
+ private
15
+
16
+ def create_main_section
17
+ sections << Section.new(:title => "Main") if sections.empty?
18
+ end
19
+ end
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/person'
2
2
 
3
3
  class SalesPerson < Person
4
+ property :commission, :integer
4
5
  end
@@ -0,0 +1,6 @@
1
+ class Section < DataMapper::Base
2
+ property :title, :string
3
+ property :created_at, :datetime
4
+
5
+ belongs_to :project
6
+ end
data/spec/models/zoo.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  class Zoo < DataMapper::Base
2
2
  property :name, :string
3
3
  property :notes, :text
4
+ property :updated_at, :datetime
4
5
 
5
6
  has_many :exhibits
6
- advanced_has_many :exhibits2, :class_name => 'Exhibit', :foreign_key => 'cow_id'
7
+
8
+ validates_presence_of :name
7
9
  end
@@ -0,0 +1,9 @@
1
+ describe('A query') do
2
+
3
+ it 'should return a simple Array of primitives' do
4
+ database.query("SELECT name FROM zoos").all? do |entry|
5
+ entry.kind_of?(String)
6
+ end.should == true
7
+ end
8
+
9
+ end
@@ -1,13 +1,77 @@
1
- describe DataMapper::Adapters::Sql::Commands::SaveCommand do
1
+ describe "Save Commands" do
2
2
 
3
3
  it "should create a new row" do
4
+ total = Zoo.all.length
4
5
  Zoo.create({ :name => 'bob' })
6
+ zoo = Zoo[:name => 'bob']
7
+ zoo.name.should == 'bob'
8
+ Zoo.all.length.should == total+1
5
9
  end
6
10
 
7
11
  it "should update an existing row" do
8
12
  dallas = Zoo[:name => 'Dallas']
9
13
  dallas.name = 'bob'
10
14
  dallas.save
15
+ dallas.name = 'Dallas'
16
+ dallas.save
17
+ end
18
+
19
+ it "should be able to read other attributes after a record is created" do
20
+ zoo = Zoo.create({ :name => 'bob' })
21
+ zoo.notes.should == nil
22
+ end
23
+
24
+ it "should stamp association on save" do
25
+ database do
26
+ dallas = Zoo[:name => 'Dallas']
27
+ dallas.exhibits << Exhibit.new(:name => 'Flying Monkeys')
28
+ dallas.save
29
+ Exhibit[:name => 'Flying Monkeys'].zoo.should == dallas
30
+ end
31
+ end
32
+
33
+ it "should be invalid if invalid associations are loaded" do
34
+ miami = Zoo[:name => 'Miami']
35
+ fish_fancy = Exhibit.new
36
+ miami.exhibits << fish_fancy
37
+ miami.should_not be_valid
38
+ fish_fancy.name = 'Fish Fancy'
39
+ fish_fancy.should be_valid
40
+ miami.should be_valid
41
+ end
42
+
43
+ it "should retrieve it's id on creation if the key is auto-incrementing so it can be successively updated" do
44
+ database do # Use the same Session so the reference-equality checks will pass.
45
+ mary = Animal::create(:name => 'Mary')
46
+ mary.name = 'Jane'
47
+ # Without retrieving the id during creation, the following #save call would fail,
48
+ # because we wouldn't know what id to update.
49
+ mary.save.should == true
50
+ jane = Animal.first(:name => 'Jane')
51
+ mary.should == jane
52
+ end
53
+ end
54
+
55
+ it "should not be dirty if there are no attributes to update" do
56
+ bob = Animal.new
57
+ bob.should_not be_dirty
58
+ bob.name = 'bob'
59
+ bob.dirty_attributes.should == { :name => 'bob' }
60
+ bob.should be_dirty
61
+ end
62
+
63
+ it "should not persist invalid objects" do
64
+ zoo = Zoo.create(:notes => "I'm invalid!")
65
+ zoo.should_not be_valid
66
+ zoo.should be_new_record
67
+ end
68
+
69
+ it "should create a Project with a \"Main\" Section" do
70
+ database do
71
+ project = Project::create(:title => 'Test')
72
+ project.sections.first.should == Section.first
73
+ project.sections.first.should be_a_kind_of(Section)
74
+ end
11
75
  end
12
76
 
13
77
  end
@@ -0,0 +1,89 @@
1
+ if ENV['ADAPTER'] == 'postgresql' && false
2
+
3
+ describe DataMapper::Adapters::PostgresqlAdapter::Mappings::Table do
4
+
5
+ before(:all) do
6
+ class Cage < DataMapper::Base
7
+ set_table_name "cages"
8
+ property :name, :string
9
+ end
10
+
11
+ class CageInSchema < DataMapper::Base
12
+ set_table_name "my_schema.cages"
13
+ property :name, :string
14
+ end
15
+ end
16
+
17
+ it "should return a quoted table name for a simple table" do
18
+ table_sql = database.adapter.table(Cage).to_sql
19
+ table_sql.should == "\"cages\""
20
+ end
21
+
22
+ it "should return a quoted schema and table name for a table which specifies a schema" do
23
+ table_sql = database.adapter.table(CageInSchema).to_sql
24
+ table_sql.should == "\"my_schema\".\"cages\""
25
+ end
26
+
27
+ it "should search only the specified schema if qualified" do
28
+ database.save(Cage)
29
+ database.adapter.table(CageInSchema).exists?.should == false
30
+ database.save(CageInSchema)
31
+ database.adapter.table(CageInSchema).exists?.should == true
32
+ end
33
+
34
+ after do
35
+ database.adapter.execute("DROP SCHEMA my_schema CASCADE") rescue nil
36
+ end
37
+
38
+ end
39
+
40
+ describe DataMapper::Adapters::PostgresqlAdapter::Commands::SaveCommand do
41
+
42
+ before(:all) do
43
+ class Cage < DataMapper::Base
44
+ set_table_name "cages"
45
+ property :name, :string
46
+ end
47
+
48
+ class CageInSchema < DataMapper::Base
49
+ set_table_name "my_schema.cages"
50
+ property :name, :string
51
+ end
52
+ end
53
+
54
+ def table_mapping_for(klass)
55
+ session = database
56
+ DataMapper::Adapters::PostgresqlAdapter::Commands::SaveCommand.new(session.adapter, session, klass)
57
+ end
58
+
59
+ it "should create a schema if it doesn't already exist" do
60
+ create_sql = table_mapping_for(CageInSchema).to_create_table_sql
61
+ create_sql.should == <<-EOS.compress_lines
62
+ CREATE SCHEMA "my_schema"; CREATE TABLE "my_schema"."cages" ("id" serial primary key, "name" varchar)
63
+ EOS
64
+ end
65
+
66
+ it "shouldn't create a schema if it exists" do
67
+ database.save(CageInSchema)
68
+ create_sql = table_mapping_for(CageInSchema).to_create_table_sql
69
+ create_sql.should == <<-EOS.compress_lines
70
+ CREATE TABLE "my_schema"."cages" ("id" serial primary key, "name" varchar)
71
+ EOS
72
+ end
73
+
74
+ it "basic crud should work in schemas" do
75
+ database.save(CageInSchema)
76
+ CageInSchema.find(:all).size.should == 0
77
+ CageInSchema.create({ :name => 'bob' })
78
+ CageInSchema.find(:all).size.should == 1
79
+ cage = CageInSchema.first(:name => 'bob')
80
+ cage.name.should == 'bob'
81
+ cage.destroy!.should == true
82
+ end
83
+
84
+ after do
85
+ database.adapter.execute("DROP SCHEMA my_schema CASCADE") rescue nil
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,27 @@
1
+ describe 'Single Table Inheritance' do
2
+
3
+ before(:all) do
4
+ fixtures(:people)
5
+ end
6
+
7
+ it "should save and load the correct Type" do
8
+ database do
9
+ ted = SalesPerson.new(:name => 'Ted')
10
+ ted.save
11
+
12
+ clone = Person.first(:name => 'Ted')
13
+ ted.should == clone
14
+
15
+ ted.should be_a_kind_of(SalesPerson)
16
+ end
17
+ end
18
+
19
+ it "secondary database should inherit the same attributes" do
20
+
21
+ database(:mock) do |db|
22
+ db.table(SalesPerson)[:name].should_not be_nil
23
+ end
24
+
25
+ end
26
+
27
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,66 +1,20 @@
1
- require File.dirname(__FILE__) + '/../lib/data_mapper'
2
- require 'yaml'
3
- require 'pp'
4
-
5
- log_path = File.dirname(__FILE__) + '/../spec.log'
6
-
7
- require 'fileutils'
8
- FileUtils::rm log_path if File.exists?(log_path)
9
-
10
- case ENV['ADAPTER']
11
- when 'sqlite3' then
12
- DataMapper::Database.setup do
13
- adapter 'sqlite3'
14
- database 'data_mapper_1.db'
15
- log_stream 'spec.log'
16
- log_level Logger::DEBUG
17
- end
18
- when 'postgresql' then
19
- DataMapper::Database.setup do
20
- adapter 'postgresql'
21
- database 'data_mapper_1'
22
- username 'postgres'
23
- log_stream 'spec.log'
24
- log_level Logger::DEBUG
25
- end
26
- else
27
- DataMapper::Database.setup do
28
- adapter 'mysql'
29
- database 'data_mapper_1'
30
- username 'root'
31
- log_stream 'spec.log'
32
- log_level Logger::DEBUG
33
- end
34
- end
35
-
36
- Dir[File.dirname(__FILE__) + '/models/*.rb'].each do |path|
37
- load path
38
- end
39
-
40
- database do |db|
41
- db.schema.each do |table|
42
- db.create_table(table.klass)
43
- end
44
- end
45
-
46
- at_exit do
47
- database do |db|
48
- db.schema.each do |table|
49
- db.drop_table(table.klass)
50
- end
51
- end
52
- end if ENV['DROP'] == '1'
1
+ ENV['LOG_NAME'] = 'spec'
2
+ require File.dirname(__FILE__) + '/../environment'
53
3
 
54
4
  # Define a fixtures helper method to load up our test data.
55
5
  def fixtures(name)
56
6
  entry = YAML::load_file(File.dirname(__FILE__) + "/fixtures/#{name}.yaml")
57
7
  klass = Kernel::const_get(Inflector.classify(Inflector.singularize(name)))
58
8
 
59
- database.schema[klass].create!
60
- klass.truncate!
9
+ database.log.debug { "AUTOMIGRATE: #{klass}" }
10
+ klass.auto_migrate!
61
11
 
62
12
  (entry.kind_of?(Array) ? entry : [entry]).each do |hash|
63
- klass::create(hash)
13
+ if hash['type']
14
+ Object::const_get(hash['type'])::create(hash)
15
+ else
16
+ klass::create(hash)
17
+ end
64
18
  end
65
19
  end
66
20
 
@@ -1,21 +1,25 @@
1
- context 'Symbolic Operators' do
1
+ describe Symbol::Operator do
2
2
 
3
- specify('should use greater_than_or_equal_to to limit results') do
3
+ before(:all) do
4
+ fixtures(:people)
5
+ end
6
+
7
+ it 'should use greater_than_or_equal_to to limit results' do
4
8
  Person.all(:age.gte => 28).size.should == 3
5
9
  end
6
10
 
7
- specify('use an Array for in-clauses') do
11
+ it 'use an Array for in-clauses' do
8
12
  family = Person.all(:id => [1, 2, 4])
9
13
  family[0].name.should == 'Sam'
10
14
  family[1].name.should == 'Amy'
11
15
  family[2].name.should == 'Josh'
12
16
  end
13
17
 
14
- specify('use "not" for not-equal operations') do
18
+ it 'use "not" for not-equal operations' do
15
19
  Person.all(:name.not => 'Bob').size.should == 4
16
20
  end
17
21
 
18
- specify('age should not be nil') do
22
+ it 'age should not be nil' do
19
23
  Person.all(:age.not => nil).size.should == 5
20
24
  end
21
25
  end