datamapper 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -1
- data/FAQ +96 -0
- data/QUICKLINKS +12 -0
- data/README +57 -155
- data/environment.rb +61 -43
- data/example.rb +30 -12
- data/lib/data_mapper.rb +6 -1
- data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
- data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
- data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
- data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
- data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
- data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
- data/lib/data_mapper/associations.rb +75 -3
- data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
- data/lib/data_mapper/associations/has_many_association.rb +168 -61
- data/lib/data_mapper/associations/has_n_association.rb +23 -3
- data/lib/data_mapper/attributes.rb +73 -0
- data/lib/data_mapper/auto_migrations.rb +2 -6
- data/lib/data_mapper/base.rb +5 -9
- data/lib/data_mapper/database.rb +4 -3
- data/lib/data_mapper/embedded_value.rb +66 -30
- data/lib/data_mapper/identity_map.rb +1 -3
- data/lib/data_mapper/is/tree.rb +121 -0
- data/lib/data_mapper/migration.rb +155 -0
- data/lib/data_mapper/persistence.rb +532 -218
- data/lib/data_mapper/property.rb +306 -0
- data/lib/data_mapper/query.rb +164 -0
- data/lib/data_mapper/support/blank.rb +2 -2
- data/lib/data_mapper/support/connection_pool.rb +5 -6
- data/lib/data_mapper/support/enumerable.rb +3 -3
- data/lib/data_mapper/support/errors.rb +10 -1
- data/lib/data_mapper/support/inflector.rb +174 -238
- data/lib/data_mapper/support/object.rb +54 -0
- data/lib/data_mapper/support/serialization.rb +19 -1
- data/lib/data_mapper/support/string.rb +7 -16
- data/lib/data_mapper/support/symbol.rb +3 -15
- data/lib/data_mapper/support/typed_set.rb +68 -0
- data/lib/data_mapper/types/base.rb +44 -0
- data/lib/data_mapper/types/string.rb +34 -0
- data/lib/data_mapper/validations/number_validator.rb +40 -0
- data/lib/data_mapper/validations/string_validator.rb +20 -0
- data/lib/data_mapper/validations/validator.rb +13 -0
- data/performance.rb +26 -1
- data/profile_data_mapper.rb +1 -1
- data/rakefile.rb +42 -2
- data/spec/acts_as_tree_spec.rb +11 -3
- data/spec/adapters/data_object_adapter_spec.rb +31 -0
- data/spec/associations/belongs_to_association_spec.rb +98 -0
- data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
- data/spec/associations/has_many_association_spec.rb +337 -0
- data/spec/attributes_spec.rb +23 -1
- data/spec/auto_migrations_spec.rb +86 -29
- data/spec/callbacks_spec.rb +107 -0
- data/spec/column_spec.rb +5 -2
- data/spec/count_command_spec.rb +33 -1
- data/spec/database_spec.rb +18 -0
- data/spec/dependency_spec.rb +4 -2
- data/spec/embedded_value_spec.rb +8 -8
- data/spec/fixtures/people.yaml +1 -1
- data/spec/fixtures/projects.yaml +10 -1
- data/spec/fixtures/tasks.yaml +6 -0
- data/spec/fixtures/tasks_tasks.yaml +2 -0
- data/spec/fixtures/tomatoes.yaml +1 -0
- data/spec/is_a_tree_spec.rb +149 -0
- data/spec/load_command_spec.rb +71 -9
- data/spec/magic_columns_spec.rb +17 -2
- data/spec/migration_spec.rb +267 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/candidate.rb +8 -0
- data/spec/models/career.rb +1 -1
- data/spec/models/chain.rb +8 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/exhibit.rb +1 -1
- data/spec/models/fence.rb +7 -0
- data/spec/models/fruit.rb +2 -2
- data/spec/models/job.rb +8 -0
- data/spec/models/person.rb +2 -3
- data/spec/models/post.rb +1 -1
- data/spec/models/project.rb +21 -1
- data/spec/models/section.rb +1 -1
- data/spec/models/serializer.rb +1 -1
- data/spec/models/task.rb +9 -0
- data/spec/models/tomato.rb +27 -0
- data/spec/models/user.rb +8 -2
- data/spec/models/zoo.rb +2 -7
- data/spec/paranoia_spec.rb +1 -1
- data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
- data/spec/postgres_spec.rb +48 -6
- data/spec/property_spec.rb +90 -9
- data/spec/query_spec.rb +71 -5
- data/spec/save_command_spec.rb +11 -0
- data/spec/spec_helper.rb +14 -11
- data/spec/support/blank_spec.rb +8 -0
- data/spec/support/inflector_spec.rb +41 -0
- data/spec/support/object_spec.rb +9 -0
- data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
- data/spec/support/silence_spec.rb +15 -0
- data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
- data/spec/support/struct_spec.rb +12 -0
- data/spec/support/typed_set_spec.rb +66 -0
- data/spec/table_spec.rb +3 -3
- data/spec/types/string.rb +81 -0
- data/spec/validates_uniqueness_of_spec.rb +17 -0
- data/spec/validations/number_validator.rb +59 -0
- data/spec/validations/string_validator.rb +14 -0
- metadata +59 -17
- data/do_performance.rb +0 -153
- data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
- data/lib/data_mapper/support/weak_hash.rb +0 -46
- data/spec/active_record_impersonation_spec.rb +0 -129
- data/spec/associations_spec.rb +0 -232
- data/spec/conditions_spec.rb +0 -49
- data/spec/has_many_association_spec.rb +0 -173
- 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
|
-
|
46
|
-
lines = target.split($/)
|
46
|
+
lines = self.dup.split($/)
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
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
|
+
|
data/performance.rb
CHANGED
@@ -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, :
|
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 } }
|