datamapper 0.1.1 → 0.2.0

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 (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