datamapper 0.2.5 → 0.3.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 (121) hide show
  1. data/CHANGELOG +5 -1
  2. data/FAQ +96 -0
  3. data/QUICKLINKS +12 -0
  4. data/README +57 -155
  5. data/environment.rb +61 -43
  6. data/example.rb +30 -12
  7. data/lib/data_mapper.rb +6 -1
  8. data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
  9. data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
  10. data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
  11. data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
  12. data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
  13. data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
  14. data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
  15. data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
  16. data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
  17. data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
  18. data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
  19. data/lib/data_mapper/associations.rb +75 -3
  20. data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
  21. data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
  22. data/lib/data_mapper/associations/has_many_association.rb +168 -61
  23. data/lib/data_mapper/associations/has_n_association.rb +23 -3
  24. data/lib/data_mapper/attributes.rb +73 -0
  25. data/lib/data_mapper/auto_migrations.rb +2 -6
  26. data/lib/data_mapper/base.rb +5 -9
  27. data/lib/data_mapper/database.rb +4 -3
  28. data/lib/data_mapper/embedded_value.rb +66 -30
  29. data/lib/data_mapper/identity_map.rb +1 -3
  30. data/lib/data_mapper/is/tree.rb +121 -0
  31. data/lib/data_mapper/migration.rb +155 -0
  32. data/lib/data_mapper/persistence.rb +532 -218
  33. data/lib/data_mapper/property.rb +306 -0
  34. data/lib/data_mapper/query.rb +164 -0
  35. data/lib/data_mapper/support/blank.rb +2 -2
  36. data/lib/data_mapper/support/connection_pool.rb +5 -6
  37. data/lib/data_mapper/support/enumerable.rb +3 -3
  38. data/lib/data_mapper/support/errors.rb +10 -1
  39. data/lib/data_mapper/support/inflector.rb +174 -238
  40. data/lib/data_mapper/support/object.rb +54 -0
  41. data/lib/data_mapper/support/serialization.rb +19 -1
  42. data/lib/data_mapper/support/string.rb +7 -16
  43. data/lib/data_mapper/support/symbol.rb +3 -15
  44. data/lib/data_mapper/support/typed_set.rb +68 -0
  45. data/lib/data_mapper/types/base.rb +44 -0
  46. data/lib/data_mapper/types/string.rb +34 -0
  47. data/lib/data_mapper/validations/number_validator.rb +40 -0
  48. data/lib/data_mapper/validations/string_validator.rb +20 -0
  49. data/lib/data_mapper/validations/validator.rb +13 -0
  50. data/performance.rb +26 -1
  51. data/profile_data_mapper.rb +1 -1
  52. data/rakefile.rb +42 -2
  53. data/spec/acts_as_tree_spec.rb +11 -3
  54. data/spec/adapters/data_object_adapter_spec.rb +31 -0
  55. data/spec/associations/belongs_to_association_spec.rb +98 -0
  56. data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
  57. data/spec/associations/has_many_association_spec.rb +337 -0
  58. data/spec/attributes_spec.rb +23 -1
  59. data/spec/auto_migrations_spec.rb +86 -29
  60. data/spec/callbacks_spec.rb +107 -0
  61. data/spec/column_spec.rb +5 -2
  62. data/spec/count_command_spec.rb +33 -1
  63. data/spec/database_spec.rb +18 -0
  64. data/spec/dependency_spec.rb +4 -2
  65. data/spec/embedded_value_spec.rb +8 -8
  66. data/spec/fixtures/people.yaml +1 -1
  67. data/spec/fixtures/projects.yaml +10 -1
  68. data/spec/fixtures/tasks.yaml +6 -0
  69. data/spec/fixtures/tasks_tasks.yaml +2 -0
  70. data/spec/fixtures/tomatoes.yaml +1 -0
  71. data/spec/is_a_tree_spec.rb +149 -0
  72. data/spec/load_command_spec.rb +71 -9
  73. data/spec/magic_columns_spec.rb +17 -2
  74. data/spec/migration_spec.rb +267 -0
  75. data/spec/models/animal.rb +1 -1
  76. data/spec/models/candidate.rb +8 -0
  77. data/spec/models/career.rb +1 -1
  78. data/spec/models/chain.rb +8 -0
  79. data/spec/models/comment.rb +1 -1
  80. data/spec/models/exhibit.rb +1 -1
  81. data/spec/models/fence.rb +7 -0
  82. data/spec/models/fruit.rb +2 -2
  83. data/spec/models/job.rb +8 -0
  84. data/spec/models/person.rb +2 -3
  85. data/spec/models/post.rb +1 -1
  86. data/spec/models/project.rb +21 -1
  87. data/spec/models/section.rb +1 -1
  88. data/spec/models/serializer.rb +1 -1
  89. data/spec/models/task.rb +9 -0
  90. data/spec/models/tomato.rb +27 -0
  91. data/spec/models/user.rb +8 -2
  92. data/spec/models/zoo.rb +2 -7
  93. data/spec/paranoia_spec.rb +1 -1
  94. data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
  95. data/spec/postgres_spec.rb +48 -6
  96. data/spec/property_spec.rb +90 -9
  97. data/spec/query_spec.rb +71 -5
  98. data/spec/save_command_spec.rb +11 -0
  99. data/spec/spec_helper.rb +14 -11
  100. data/spec/support/blank_spec.rb +8 -0
  101. data/spec/support/inflector_spec.rb +41 -0
  102. data/spec/support/object_spec.rb +9 -0
  103. data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
  104. data/spec/support/silence_spec.rb +15 -0
  105. data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
  106. data/spec/support/struct_spec.rb +12 -0
  107. data/spec/support/typed_set_spec.rb +66 -0
  108. data/spec/table_spec.rb +3 -3
  109. data/spec/types/string.rb +81 -0
  110. data/spec/validates_uniqueness_of_spec.rb +17 -0
  111. data/spec/validations/number_validator.rb +59 -0
  112. data/spec/validations/string_validator.rb +14 -0
  113. metadata +59 -17
  114. data/do_performance.rb +0 -153
  115. data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
  116. data/lib/data_mapper/support/weak_hash.rb +0 -46
  117. data/spec/active_record_impersonation_spec.rb +0 -129
  118. data/spec/associations_spec.rb +0 -232
  119. data/spec/conditions_spec.rb +0 -49
  120. data/spec/has_many_association_spec.rb +0 -173
  121. data/spec/models/animals_exhibit.rb +0 -8
@@ -0,0 +1,54 @@
1
+ module DataMapper
2
+ module Support
3
+ module Object
4
+
5
+ def self.included(base)
6
+
7
+ nested_constants = Hash.new do |h,k|
8
+ klass = Object
9
+ k.split('::').each do |c|
10
+ klass = klass.const_get(c)
11
+ end
12
+ h[k] = klass
13
+ end
14
+
15
+ base.instance_variable_set("@nested_constants", nested_constants)
16
+ base.send(:include, ClassMethods)
17
+ end
18
+
19
+ module ClassMethods
20
+ def recursive_const_get(nested_name)
21
+ @nested_constants[nested_name]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ class Object #:nodoc:
29
+ include DataMapper::Support::Object
30
+ end
31
+
32
+ # require 'benchmark'
33
+ #
34
+ # N = 1_000_000
35
+ #
36
+ # puts Benchmark.measure {
37
+ # N.times { Object.recursive_const_get('DataMapper::Support::Object') }
38
+ # }
39
+ #
40
+ # puts Benchmark.measure {
41
+ # N.times {
42
+ # klass = Object
43
+ # 'DataMapper::Support::Object'.split('::').each do |c|
44
+ # klass = klass.const_get(c)
45
+ # end
46
+ # klass
47
+ # }
48
+ # }
49
+ #
50
+ # __END__
51
+ # >>> object.rb
52
+ #
53
+ # 0.910000 0.000000 0.910000 ( 0.916914)
54
+ # 6.140000 0.010000 6.150000 ( 6.151984)
@@ -1,5 +1,11 @@
1
1
  require 'rexml/document'
2
2
 
3
+ begin
4
+ require 'faster_csv'
5
+ rescue LoadError
6
+ nil
7
+ end
8
+
3
9
  begin
4
10
  require 'json/ext'
5
11
  rescue LoadError
@@ -31,11 +37,16 @@ module DataMapper
31
37
  to_xml_document.to_s
32
38
  end
33
39
 
40
+ def xml_element_name ## overloadable
41
+ Inflector.underscore(self.class.name)
42
+ end
43
+
34
44
  def to_xml_document
35
45
  doc = REXML::Document.new
36
46
 
37
47
  table = database_context.table(self.class)
38
- root = doc.add_element(Inflector.underscore(self.class.name))
48
+ # root = doc.add_element(Inflector.underscore(self.class.name))
49
+ root = doc.add_element(xml_element_name)
39
50
 
40
51
  key_attribute = root.attributes << REXML::Attribute.new(table.key.to_s, key)
41
52
 
@@ -69,6 +80,13 @@ module DataMapper
69
80
  result
70
81
  end
71
82
 
83
+ def to_csv(writer = "")
84
+ FasterCSV.generate(writer) do |csv|
85
+ csv << database_context.table(self.class).columns.map { |column| get_value_for_column(column) }
86
+ end
87
+ return writer
88
+ end
89
+
72
90
  def get_value_for_column(column)
73
91
  send(column.type == :boolean ? column.name.to_s.ensure_ends_with('?') : column.name)
74
92
  end
@@ -41,26 +41,17 @@ module DataMapper
41
41
  split($/).map { |line| line.strip }.join(spaced ? ' ' : '')
42
42
  end
43
43
 
44
+ # Useful for heredocs - removes whitespace margin.
44
45
  def margin(indicator = nil)
45
- target = dup
46
- lines = target.split($/)
46
+ lines = self.dup.split($/)
47
47
 
48
- if indicator.nil?
49
- min_margin = nil
50
- lines.each do |line|
51
- if line =~ /(\s+)/ && (min_margin.nil? || $1.size < min_margin)
52
- min_margin = $1.size
53
- end
48
+ min_margin = 0
49
+ lines.each do |line|
50
+ if line =~ /^(\s+)/ && (min_margin == 0 || $1.size < min_margin)
51
+ min_margin = $1.size
54
52
  end
55
-
56
- lines.map do |line|
57
- line.sub(/^\s{#{min_margin}}/, '')
58
- end.join($/)
59
- else
60
- lines.map do |line|
61
- line.sub(/^.*?#{"\\" + indicator}/, '')
62
- end.join($/)
63
53
  end
54
+ lines.map { |line| line.sub(/^\s{#{min_margin}}/, '') }.join($/)
64
55
  end
65
56
 
66
57
  # Formats String for easy translation. Replaces an arbitrary number of
@@ -48,23 +48,11 @@ module DataMapper
48
48
  def in
49
49
  Operator.new(self, :in)
50
50
  end
51
-
52
- def select(klass = nil)
53
- Operator.new(self, :select, { :class => klass })
54
- end
55
-
56
- def to_s
57
- @string_form || (@string_form = id2name.freeze)
58
- end
59
-
51
+
60
52
  def to_proc
61
- lambda { |value| value.send(self) }
53
+ lambda { |value| value.send(self) }
62
54
  end
63
55
 
64
- def as_instance_variable_name
65
- @instance_variable_name_form || (@instance_variable_name_form = "@#{id2name}".freeze)
66
- end
67
-
68
56
  # Calculations:
69
57
 
70
58
  def count
@@ -91,4 +79,4 @@ end # module DataMapper
91
79
 
92
80
  class Symbol #:nodoc:
93
81
  include DataMapper::Support::Symbol
94
- end
82
+ end
@@ -0,0 +1,68 @@
1
+ require 'set'
2
+
3
+ module DataMapper
4
+ module Support
5
+ class TypedSet
6
+
7
+ include ::Enumerable
8
+
9
+ def initialize(*types)
10
+ @types = types
11
+ @set = SortedSet.new
12
+ end
13
+
14
+ def <<(item)
15
+ raise ArgumentError.new("#{item.inspect} must be a kind of: #{@types.inspect}") unless @types.any? { |type| type === item }
16
+ @set << item
17
+ return self
18
+ end
19
+
20
+ def concat(values)
21
+ [*values].each { |item| self << item }
22
+ self
23
+ end
24
+
25
+ def inspect
26
+ "#<DataMapper::Support::TypedSet#{@types.inspect}: {#{entries.inspect[1...-1]}}>"
27
+ end
28
+
29
+ def each
30
+ @set.each { |item| yield(item) }
31
+ end
32
+
33
+ def delete?(item)
34
+ @set.delete?(item)
35
+ end
36
+
37
+ def size
38
+ @set.size
39
+ end
40
+ alias length size
41
+
42
+ def empty?
43
+ @set.empty?
44
+ end
45
+ alias blank? empty?
46
+
47
+ def clear
48
+ @set.clear
49
+ end
50
+
51
+ def +(other)
52
+ x = self.class.new(*@types)
53
+ each { |entry| x << entry }
54
+ other.each { |entry| x << entry } unless other.blank?
55
+ return x
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ class Class
62
+
63
+ include Comparable
64
+
65
+ def <=>(other)
66
+ name <=> other.name
67
+ end
68
+ end
@@ -0,0 +1,44 @@
1
+ require 'data_mapper/validations/number_validator'
2
+ require 'data_mapper/validations/string_validator'
3
+
4
+ module DataMapper
5
+ module Types
6
+ TYPE_MAP = {}
7
+
8
+ module Base
9
+ module ClassMethods
10
+ def context(ctx)
11
+ contexts(ctx)
12
+ end
13
+
14
+ def contexts(*ctxs)
15
+ @contexts || @contexts = %w{__all__} << ctxs.map { |c| c.to_s }
16
+ end
17
+ end
18
+
19
+ def self.included(klass)
20
+ klass.extend(ClassMethods)
21
+ end
22
+
23
+ def do_validations
24
+ raise NotImplementedError.new
25
+ end
26
+
27
+ def valid?(context = "__all__")
28
+ @errors = []
29
+
30
+ if self.class.contexts.include?(context.to_s)
31
+ do_validations
32
+
33
+ @errors.empty?
34
+ else
35
+ true
36
+ end
37
+ end
38
+
39
+ def errors
40
+ @errors || @errors = []
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ require 'data_mapper/types/base'
2
+
3
+ module DataMapper
4
+ module Types
5
+ class String < ::String
6
+ include Types::Base
7
+
8
+ TYPE_MAP[:string] = self
9
+ TYPE_MAP[::String] = self
10
+
11
+ def self.length
12
+ length_validator
13
+ end
14
+
15
+ def self.matches(regexp)
16
+ match_validator.matches(regexp)
17
+ end
18
+
19
+ def self.length_validator
20
+ @length_validator ||
21
+ @length_validator = Validations::NumberValidator.new
22
+ end
23
+
24
+ def self.match_validator
25
+ @match_validator || @match_validator = Validations::StringValidator.new
26
+ end
27
+
28
+ def do_validations
29
+ errors.concat(self.class.length_validator.errors_for(length))
30
+ errors.concat(self.class.match_validator.errors_for(self))
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ require 'data_mapper/validations/validator'
2
+
3
+ module DataMapper
4
+ module Validations
5
+ class NumberValidator < Validator
6
+ def <(max)
7
+ @max_excl = max
8
+ end
9
+
10
+ def <=(max)
11
+ @max_incl = max
12
+ end
13
+
14
+ def >(min)
15
+ @min_excl = min
16
+ end
17
+
18
+ def >=(min)
19
+ @min_incl = min
20
+ end
21
+
22
+ def between(range)
23
+ @range = range
24
+ end
25
+
26
+ def errors_for(target)
27
+ errors = []
28
+ error = nil
29
+
30
+ errors << Validator::Error.new(@max_excl, target) if @max_excl && target >= @max_excl
31
+ errors << Validator::Error.new(@max_incl, target) if @max_incl && target > @max_incl
32
+ errors << Validator::Error.new(@min_excl, target) if @min_excl && target <= @min_excl
33
+ errors << Validator::Error.new(@min_incl, target) if @min_incl && target < @min_incl
34
+ errors << Validator::Error.new(@range, target) if @range && ! @range.include?(target)
35
+
36
+ errors
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ require 'data_mapper/validations/validator'
2
+
3
+ module DataMapper
4
+ module Validations
5
+ class StringValidator < Validator
6
+ def matches(regexp)
7
+ @regexp = regexp
8
+ end
9
+
10
+ def errors_for(target)
11
+ errors = []
12
+
13
+ errors << Validator::Error.new(@regexp, target) if @regexp && @regexp.match(target)
14
+
15
+ errors
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,13 @@
1
+ module DataMapper
2
+ module Validations
3
+ class Validator
4
+ Error = Struct.new(:expected, :got)
5
+
6
+ def errors_for(target)
7
+ raise NotImplementedError.new
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+
@@ -35,7 +35,7 @@ DataMapper::Database.setup(configuration_options)
35
35
  class DMAnimal < DataMapper::Base #:nodoc:
36
36
  set_table_name 'animals'
37
37
  property :name, :string
38
- property :notes, :string
38
+ property :notes, :text
39
39
  end
40
40
 
41
41
  class DMPerson < DataMapper::Base #:nodoc:
@@ -71,6 +71,7 @@ end
71
71
 
72
72
  class Zoo < DataMapper::Base #:nodoc:
73
73
  property :name, :string
74
+ property :notes, :text
74
75
  has_many :exhibits
75
76
  end
76
77
 
@@ -100,6 +101,18 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
100
101
  N.times { ARAnimal.find(1).name }
101
102
  end
102
103
 
104
+ x.report('ActiveRecord:id:raw_result-set') do
105
+ connection = ARAnimal.connection
106
+
107
+ N.times do
108
+ result = connection.execute("SELECT name FROM animals WHERE id = 1")
109
+ result.each do |row|
110
+ nil
111
+ end
112
+ result.free if result.respond_to?(:free)
113
+ end
114
+ end
115
+
103
116
  x.report('DataMapper:id') do
104
117
  N.times { DMAnimal[1].name }
105
118
  end
@@ -109,6 +122,18 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
109
122
  N.times { DMAnimal[1].name }
110
123
  end
111
124
  end
125
+
126
+ x.report('DataMapper:id:raw-result-set') do
127
+ database.adapter.connection do |db|
128
+ N.times do
129
+ command = db.create_command("SELECT name FROM animals WHERE id = 1")
130
+
131
+ command.execute_reader do |reader|
132
+ reader.each { reader.current_row }
133
+ end
134
+ end
135
+ end
136
+ end
112
137
 
113
138
  x.report('ActiveRecord:all') do
114
139
  N.times { ARZoo.find(:all).each { |a| a.name } }
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  profile do
17
17
  1000.times do
18
- database.query("SELECT * FROM zoos")
18
+ Zoo.all.each { |zoo| zoo.name; zoo.exhibits.entries }
19
19
  end
20
20
  end
21
21