sam-dm-core 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/Manifest.txt +5 -0
  2. data/QUICKLINKS +1 -2
  3. data/Rakefile +3 -3
  4. data/SPECS +9 -10
  5. data/lib/dm-core.rb +15 -22
  6. data/lib/dm-core/adapters.rb +18 -0
  7. data/lib/dm-core/adapters/abstract_adapter.rb +17 -10
  8. data/lib/dm-core/adapters/data_objects_adapter.rb +18 -16
  9. data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
  10. data/lib/dm-core/adapters/postgres_adapter.rb +2 -2
  11. data/lib/dm-core/adapters/sqlite3_adapter.rb +1 -1
  12. data/lib/dm-core/associations.rb +3 -2
  13. data/lib/dm-core/associations/many_to_many.rb +2 -2
  14. data/lib/dm-core/associations/many_to_one.rb +1 -1
  15. data/lib/dm-core/associations/one_to_many.rb +13 -7
  16. data/lib/dm-core/associations/relationship.rb +20 -15
  17. data/lib/dm-core/auto_migrations.rb +4 -12
  18. data/lib/dm-core/collection.rb +9 -5
  19. data/lib/dm-core/dependency_queue.rb +2 -1
  20. data/lib/dm-core/identity_map.rb +3 -6
  21. data/lib/dm-core/model.rb +44 -27
  22. data/lib/dm-core/property.rb +3 -13
  23. data/lib/dm-core/property_set.rb +29 -22
  24. data/lib/dm-core/query.rb +49 -47
  25. data/lib/dm-core/repository.rb +3 -3
  26. data/lib/dm-core/resource.rb +12 -12
  27. data/lib/dm-core/scope.rb +7 -7
  28. data/lib/dm-core/support/kernel.rb +6 -2
  29. data/lib/dm-core/transaction.rb +7 -7
  30. data/lib/dm-core/version.rb +1 -1
  31. data/script/performance.rb +109 -30
  32. data/script/profile.rb +2 -2
  33. data/spec/integration/association_spec.rb +13 -1
  34. data/spec/integration/associations/one_to_many_spec.rb +40 -3
  35. data/spec/integration/auto_migrations_spec.rb +16 -1
  36. data/spec/integration/dependency_queue_spec.rb +0 -12
  37. data/spec/integration/postgres_adapter_spec.rb +1 -1
  38. data/spec/integration/property_spec.rb +4 -4
  39. data/spec/integration/resource_spec.rb +6 -0
  40. data/spec/integration/sti_spec.rb +22 -0
  41. data/spec/integration/strategic_eager_loading_spec.rb +21 -6
  42. data/spec/integration/type_spec.rb +1 -1
  43. data/spec/lib/model_loader.rb +10 -1
  44. data/spec/models/zoo.rb +1 -0
  45. data/spec/spec_helper.rb +3 -2
  46. data/spec/unit/adapters/data_objects_adapter_spec.rb +3 -3
  47. data/spec/unit/associations/many_to_many_spec.rb +16 -1
  48. data/spec/unit/associations/many_to_one_spec.rb +9 -2
  49. data/spec/unit/model_spec.rb +12 -30
  50. data/spec/unit/property_set_spec.rb +8 -1
  51. data/spec/unit/query_spec.rb +41 -0
  52. data/spec/unit/resource_spec.rb +27 -4
  53. data/spec/unit/transaction_spec.rb +13 -13
  54. data/tasks/ci.rb +4 -36
  55. data/tasks/dm.rb +3 -3
  56. metadata +7 -16
data/lib/dm-core/query.rb CHANGED
@@ -99,9 +99,12 @@ module DataMapper
99
99
  bind_values
100
100
  end
101
101
 
102
- # TODO: spec this
103
- def inheritance_property_index(repository)
104
- fields.index(model.inheritance_property(repository.name))
102
+ def inheritance_property
103
+ fields.detect { |property| property.type == DataMapper::Types::Discriminator }
104
+ end
105
+
106
+ def inheritance_property_index
107
+ fields.index(inheritance_property)
105
108
  end
106
109
 
107
110
  # TODO: spec this
@@ -190,7 +193,7 @@ module DataMapper
190
193
  @includes = normalize_includes(@includes)
191
194
 
192
195
  # treat all non-options as conditions
193
- (options.keys - OPTIONS - OPTIONS.map { |option| option.to_s }).each do |k|
196
+ (options.keys - OPTIONS).each do |k|
194
197
  append_condition(k, options[k])
195
198
  end
196
199
 
@@ -218,53 +221,50 @@ module DataMapper
218
221
 
219
222
  # validate the options
220
223
  def assert_valid_options(options)
221
- # validate the reload option and unique option
222
- ([ :reload, :unique ] & options.keys).each do |attribute|
223
- if options[attribute] != true && options[attribute] != false
224
- raise ArgumentError, "+options[:#{attribute}]+ must be true or false, but was #{options[attribute].inspect}", caller(2)
225
- end
226
- end
227
-
228
- # validate the offset and limit options
229
- ([ :offset, :limit ] & options.keys).each do |attribute|
230
- value = options[attribute]
231
- assert_kind_of "options[:#{attribute}]", value, Integer
232
- end
233
-
234
- if options.has_key?(:offset) && options[:offset] < 0
235
- raise ArgumentError, "+options[:offset]+ must be greater than or equal to 0, but was #{options[:offset].inspect}", caller(2)
236
- end
237
-
238
- if options.has_key?(:limit) && options[:limit] < 1
239
- raise ArgumentError, "+options[:limit]+ must be greater than or equal to 1, but was #{options[:limit].inspect}", caller(2)
240
- end
224
+ # [DB] This might look more ugly now, but it's 2x as fast as the old code
225
+ # [DB] This is one of the heavy spots for Query.new I found during profiling.
226
+ options.each_pair do |attribute, value|
227
+
228
+ # validate the reload option and unique option
229
+ if [:reload, :unique].include? attribute
230
+ if value != true && value != false
231
+ raise ArgumentError, "+options[:#{attribute}]+ must be true or false, but was #{value.inspect}", caller(2)
232
+ end
241
233
 
242
- # validate the order, fields, links, includes and conditions options
243
- ([ :order, :fields, :links, :includes ] & options.keys).each do |attribute|
244
- value = options[attribute]
245
- assert_kind_of "options[:#{attribute}]", value, Array
234
+ # validate the offset and limit options
235
+ elsif [:offset, :limit].include? attribute
236
+ assert_kind_of "options[:#{attribute}]", value, Integer
237
+ if attribute == :offset && value < 0
238
+ raise ArgumentError, "+options[:offset]+ must be greater than or equal to 0, but was #{value.inspect}", caller(2)
239
+ elsif attribute == :limit && value < 1
240
+ raise ArgumentError, "+options[:limit]+ must be greater than or equal to 1, but was #{options[:limit].inspect}", caller(2)
241
+ end
246
242
 
247
- if value.empty?
248
- if attribute == :fields
249
- if options[:unique] == false
250
- raise ArgumentError, '+options[:fields]+ cannot be empty if +options[:unique] is false', caller(2)
251
- end
252
- elsif attribute == :order
253
- if options[:fields] && options[:fields].any? { |p| !p.kind_of?(Operator) }
254
- raise ArgumentError, '+options[:order]+ cannot be empty if +options[:fields] contains a non-operator', caller(2)
243
+ # validate the :order, :fields, :links and :includes options
244
+ elsif [ :order, :fields, :links, :includes ].include? attribute
245
+ assert_kind_of "options[:#{attribute}]", value, Array
246
+
247
+ if value.empty?
248
+ if attribute == :fields
249
+ if options[:unique] == false
250
+ raise ArgumentError, '+options[:fields]+ cannot be empty if +options[:unique] is false', caller(2)
251
+ end
252
+ elsif attribute == :order
253
+ if options[:fields] && options[:fields].any? { |p| !p.kind_of?(Operator) }
254
+ raise ArgumentError, '+options[:order]+ cannot be empty if +options[:fields] contains a non-operator', caller(2)
255
+ end
256
+ else
257
+ raise ArgumentError, "+options[:#{attribute}]+ cannot be empty", caller(2)
255
258
  end
256
- else
257
- raise ArgumentError, "+options[:#{attribute}]+ cannot be empty", caller(2)
258
259
  end
259
- end
260
- end
261
260
 
262
- if options.has_key?(:conditions)
263
- value = options[:conditions]
264
- assert_kind_of 'options[:conditions]', value, Hash, Array
261
+ # validates the :conditions option
262
+ elsif :conditions == attribute
263
+ assert_kind_of 'options[:conditions]', value, Hash, Array
265
264
 
266
- if value.empty?
267
- raise ArgumentError, '+options[:conditions]+ cannot be empty', caller(2)
265
+ if value.empty?
266
+ raise ArgumentError, '+options[:conditions]+ cannot be empty', caller(2)
267
+ end
268
268
  end
269
269
  end
270
270
  end
@@ -464,10 +464,11 @@ module DataMapper
464
464
 
465
465
  # build an index of conditions by the property and operator to
466
466
  # avoid nested looping
467
- conditions_index = Hash.new { |h,k| h[k] = {} }
467
+ conditions_index = {}
468
468
  @conditions.each do |condition|
469
469
  operator, property = *condition
470
470
  next if :raw == operator
471
+ conditions_index[property] ||= {}
471
472
  conditions_index[property][operator] = condition
472
473
  end
473
474
 
@@ -477,6 +478,7 @@ module DataMapper
477
478
  other_operator, other_property, other_bind_value = *other_condition
478
479
 
479
480
  unless :raw == other_operator
481
+ conditions_index[other_property] ||= {}
480
482
  if condition = conditions_index[other_property][other_operator]
481
483
  operator, property, bind_value = *condition
482
484
 
@@ -571,7 +573,7 @@ module DataMapper
571
573
  class Path
572
574
  include Assertions
573
575
 
574
- %w[ id type ].each { |m| undef_method m }
576
+ instance_methods.each { |m| undef_method m if %w[ id type ].include?(m.to_s) }
575
577
 
576
578
  attr_reader :relationships, :model, :property, :operator
577
579
 
@@ -33,11 +33,11 @@ module DataMapper
33
33
  end
34
34
 
35
35
  def identity_map(model)
36
- @identity_maps[model]
36
+ @identity_maps[model] ||= IdentityMap.new
37
37
  end
38
38
 
39
39
  # TODO: spec this
40
- def scope(&block)
40
+ def scope
41
41
  Repository.context << self
42
42
 
43
43
  begin
@@ -97,7 +97,7 @@ module DataMapper
97
97
  assert_kind_of 'name', name, Symbol
98
98
 
99
99
  @name = name
100
- @identity_maps = Hash.new { |h,model| h[model] = IdentityMap.new }
100
+ @identity_maps = {}
101
101
  end
102
102
 
103
103
  # TODO: move to dm-more/dm-migrations
@@ -198,7 +198,7 @@ module DataMapper
198
198
  attrs = []
199
199
 
200
200
  properties.each do |property|
201
- value = if property.lazy? && !attribute_loaded?(property.name) && !new_record?
201
+ value = if !attribute_loaded?(property.name) && !new_record?
202
202
  '<not loaded>'
203
203
  else
204
204
  send(property.getter).inspect
@@ -277,9 +277,7 @@ module DataMapper
277
277
  associations_saved = false
278
278
  child_associations.each { |a| associations_saved |= a.save }
279
279
 
280
- saved = if dirty? || (new_record? && key_properties.any? { |p| p.serial? })
281
- new_record? ? create : update
282
- end
280
+ saved = new_record? ? create : update
283
281
 
284
282
  if saved
285
283
  original_values.clear
@@ -493,13 +491,13 @@ module DataMapper
493
491
  # --
494
492
  # @api public
495
493
  def attributes=(values_hash)
496
- values_hash.each_pair do |k,v|
497
- setter = "#{k.to_s.sub(/\?\z/, '')}="
494
+ values_hash.each do |name, value|
495
+ name = name.to_s.sub(/\?\z/, '')
498
496
 
499
- if respond_to?(setter)
500
- send(setter, v)
497
+ if self.class.public_method_defined?(setter = "#{name}=")
498
+ send(setter, value)
501
499
  else
502
- raise NameError, "#{setter} is not a public property"
500
+ raise ArgumentError, "The property '#{name}' is not a public property."
503
501
  end
504
502
  end
505
503
  end
@@ -546,6 +544,8 @@ module DataMapper
546
544
 
547
545
  # Needs to be a protected method so that it is hookable
548
546
  def create
547
+ # Can't create a resource that is not dirty and doesn't have serial keys
548
+ return false if new_record? && !dirty? && !model.key.any? { |p| p.serial? }
549
549
  # set defaults for new resource
550
550
  properties.each do |property|
551
551
  next if attribute_loaded?(property.name)
@@ -588,7 +588,7 @@ module DataMapper
588
588
  raise IncompleteResourceError, "#{model.name} must have a key."
589
589
  end
590
590
 
591
- class << self; @_valid_model = true; end
591
+ self.class.instance_variable_set("@_valid_model", true)
592
592
  end
593
593
 
594
594
  # TODO document
@@ -627,8 +627,8 @@ module DataMapper
627
627
  # @api public
628
628
  #
629
629
  # TODO: move to dm-more/dm-transactions
630
- def transaction(&block)
631
- model.transaction(&block)
630
+ def transaction
631
+ model.transaction { |*block_args| yield(*block_args) }
632
632
  end
633
633
  end # module Transaction
634
634
 
data/lib/dm-core/scope.rb CHANGED
@@ -5,8 +5,8 @@ module DataMapper
5
5
  # @api private
6
6
  def default_scope(repository_name = nil)
7
7
  repository_name = self.default_repository_name if repository_name == :default || repository_name.nil?
8
- @default_scope ||= Hash.new{|h,k| h[k] = {}}
9
- @default_scope[repository_name]
8
+ @default_scope ||= {}
9
+ @default_scope[repository_name] ||= {}
10
10
  end
11
11
 
12
12
  # @api private
@@ -17,13 +17,13 @@ module DataMapper
17
17
  protected
18
18
 
19
19
  # @api semipublic
20
- def with_scope(query, &block)
20
+ def with_scope(query)
21
21
  # merge the current scope with the passed in query
22
- with_exclusive_scope(self.query ? self.query.merge(query) : query, &block)
22
+ with_exclusive_scope(self.query ? self.query.merge(query) : query) {|*block_args| yield(*block_args) }
23
23
  end
24
24
 
25
25
  # @api semipublic
26
- def with_exclusive_scope(query, &block)
26
+ def with_exclusive_scope(query)
27
27
  query = DataMapper::Query.new(repository, self, query) if query.kind_of?(Hash)
28
28
 
29
29
  scope_stack << query
@@ -44,8 +44,8 @@ module DataMapper
44
44
 
45
45
  # @api private
46
46
  def scope_stack
47
- scope_stack_for = Thread.current[:dm_scope_stack] ||= Hash.new { |h,model| h[model] = [] }
48
- scope_stack_for[self]
47
+ scope_stack_for = Thread.current[:dm_scope_stack] ||= {}
48
+ scope_stack_for[self] ||= []
49
49
  end
50
50
 
51
51
  # @api private
@@ -1,7 +1,11 @@
1
1
  module Kernel
2
2
  # Delegates to DataMapper::repository.
3
3
  # Will not overwrite if a method of the same name is pre-defined.
4
- def repository(*args, &block)
5
- DataMapper.repository(*args, &block)
4
+ def repository(*args)
5
+ if block_given?
6
+ DataMapper.repository(*args) { |*block_args| yield(*block_args) }
7
+ else
8
+ DataMapper.repository(*args)
9
+ end
6
10
  end
7
11
  end # module Kernel
@@ -13,12 +13,12 @@ module DataMapper
13
13
  # In fact, it just calls #link with the given arguments at the end of the
14
14
  # constructor.
15
15
  #
16
- def initialize(*things, &block)
16
+ def initialize(*things)
17
17
  @transaction_primitives = {}
18
18
  @state = :none
19
19
  @adapters = {}
20
20
  link(*things)
21
- commit(&block) if block_given?
21
+ commit { |*block_args| yield(*block_args) } if block_given?
22
22
  end
23
23
 
24
24
  #
@@ -39,7 +39,7 @@ module DataMapper
39
39
  # within this transaction. The transaction will begin and commit around
40
40
  # the block, and rollback if an exception is raised.
41
41
  #
42
- def link(*things, &block)
42
+ def link(*things)
43
43
  raise "Illegal state for link: #{@state}" unless @state == :none
44
44
  things.each do |thing|
45
45
  if thing.is_a?(Array)
@@ -56,7 +56,7 @@ module DataMapper
56
56
  raise "Unknown argument to #{self}#link: #{thing.inspect}"
57
57
  end
58
58
  end
59
- return commit(&block) if block_given?
59
+ return commit { |*block_args| yield(*block_args) } if block_given?
60
60
  return self
61
61
  end
62
62
 
@@ -83,12 +83,12 @@ module DataMapper
83
83
  # If no block is given, it will simply commit any changes made since the
84
84
  # Transaction did #begin.
85
85
  #
86
- def commit(&block)
86
+ def commit
87
87
  if block_given?
88
88
  raise "Illegal state for commit with block: #{@state}" unless @state == :none
89
89
  begin
90
90
  self.begin
91
- rval = within(&block)
91
+ rval = within { |*block_args| yield(*block_args) }
92
92
  self.commit if @state == :begin
93
93
  return rval
94
94
  rescue Exception => e
@@ -128,7 +128,7 @@ module DataMapper
128
128
  # adapter it is associated with, and it will ensures that it will pop the
129
129
  # Transaction away again after the block is finished.
130
130
  #
131
- def within(&block)
131
+ def within
132
132
  raise "No block provided" unless block_given?
133
133
  raise "Illegal state for within: #{@state}" unless @state == :begin
134
134
  @adapters.each do |adapter, state|
@@ -1,3 +1,3 @@
1
1
  module DataMapper
2
- VERSION = '0.9.6' unless defined?(DataMapper::VERSION)
2
+ VERSION = '0.9.7' unless defined?(DataMapper::VERSION)
3
3
  end
@@ -1,19 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-core')
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'dm-core', 'version')
4
5
 
5
- require 'rubygems'
6
6
  require 'ftools'
7
+ require 'rubygems'
7
8
 
8
- # sudo gem install rbench
9
- # OR git clone git://github.com/somebee/rbench.git , rake install
10
- gem 'rbench', '>=0.2.2'
9
+ gem 'rbench', '~>0.2.3'
11
10
  require 'rbench'
12
11
 
13
- gem 'faker', '>=0.3.1'
12
+ gem 'faker', '~>0.3.1'
14
13
  require 'faker'
15
14
 
16
- gem 'activerecord', '>=2.1.0'
15
+ gem 'activerecord', '~>2.2.2'
17
16
  require 'active_record'
18
17
 
19
18
  socket_file = Pathname.glob(%w[
@@ -38,11 +37,11 @@ configuration_options[:socket] = socket_file unless socket_file.nil?
38
37
  log_dir = DataMapper.root / 'log'
39
38
  log_dir.mkdir unless log_dir.directory?
40
39
 
41
- DataMapper::Logger.new(log_dir / 'dm.log', :debug)
40
+ DataMapper::Logger.new(log_dir / 'dm.log', :off)
42
41
  adapter = DataMapper.setup(:default, "mysql://root@localhost/data_mapper_1?socket=#{socket_file}")
43
42
 
44
43
  if configuration_options[:adapter]
45
- sqlfile = File.join(File.dirname(__FILE__),'..','tmp','perf.sql')
44
+ sqlfile = File.join(File.dirname(__FILE__),'..','tmp','performance.sql')
46
45
  mysql_bin = %w[mysql mysql5].select{|bin| `which #{bin}`.length > 0 }
47
46
  mysqldump_bin = %w[mysqldump mysqldump5].select{|bin| `which #{bin}`.length > 0 }
48
47
  end
@@ -54,6 +53,15 @@ ActiveRecord::Base.establish_connection(configuration_options)
54
53
 
55
54
  class ARExhibit < ActiveRecord::Base #:nodoc:
56
55
  set_table_name 'exhibits'
56
+
57
+ belongs_to :user, :class_name => 'ARUser', :foreign_key => 'user_id'
58
+ end
59
+
60
+ class ARUser < ActiveRecord::Base #:nodoc:
61
+ set_table_name 'users'
62
+
63
+ has_many :exhibits, :foreign_key => 'user_id'
64
+
57
65
  end
58
66
 
59
67
  ARExhibit.find_by_sql('SELECT 1')
@@ -64,17 +72,39 @@ class Exhibit
64
72
  property :id, Serial
65
73
  property :name, String
66
74
  property :zoo_id, Integer
75
+ property :user_id, Integer
67
76
  property :notes, Text, :lazy => true
68
77
  property :created_on, Date
78
+
79
+ belongs_to :user
69
80
  # property :updated_at, DateTime
70
81
  end
71
82
 
83
+ class User
84
+ include DataMapper::Resource
85
+
86
+ property :id, Serial
87
+ property :name, String
88
+ property :email, String
89
+ property :about, Text, :lazy => true
90
+ property :created_on, Date
91
+
92
+ end
93
+
72
94
  touch_attributes = lambda do |exhibits|
73
95
  [*exhibits].each do |exhibit|
74
96
  exhibit.id
75
97
  exhibit.name
76
98
  exhibit.created_on
77
- # exhibit.updated_at
99
+ end
100
+ end
101
+
102
+ touch_relationships = lambda do |exhibits|
103
+ [*exhibits].each do |exhibit|
104
+ exhibit.id
105
+ exhibit.name
106
+ exhibit.created_on
107
+ exhibit.user
78
108
  end
79
109
  end
80
110
 
@@ -87,27 +117,49 @@ if sqlfile && File.exists?(sqlfile)
87
117
  `#{mysql_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} < #{sqlfile}`
88
118
  else
89
119
 
120
+ puts "Generating data for benchmarking..."
121
+
122
+ User.auto_migrate!
90
123
  Exhibit.auto_migrate!
91
124
 
125
+ users = []
92
126
  exhibits = []
127
+
93
128
  # pre-compute the insert statements and fake data compilation,
94
129
  # so the benchmarks below show the actual runtime for the execute
95
130
  # method, minus the setup steps
96
- 10_000.times do
131
+
132
+ # Using the same paragraph for all exhibits because it is very slow
133
+ # to generate unique paragraphs for all exhibits.
134
+ paragraph = Faker::Lorem.paragraphs.join($/)
135
+
136
+ 10_000.times do |i|
137
+ users << [
138
+ 'INSERT INTO `users` (`name`,`email`,`created_on`) VALUES (?, ?, ?)',
139
+ Faker::Name.name,
140
+ Faker::Internet.email,
141
+ Date.today
142
+ ]
143
+
97
144
  exhibits << [
98
- 'INSERT INTO `exhibits` (`name`, `zoo_id`, `notes`, `created_on`) VALUES (?, ?, ?, ?)',
145
+ 'INSERT INTO `exhibits` (`name`, `zoo_id`, `user_id`, `notes`, `created_on`) VALUES (?, ?, ?, ?, ?)',
99
146
  Faker::Company.name,
100
147
  rand(10).ceil,
101
- Faker::Lorem.paragraphs.join($/),
148
+ i,
149
+ paragraph,#Faker::Lorem.paragraphs.join($/),
102
150
  Date.today
103
151
  ]
104
152
  end
153
+
154
+ puts "Inserting 10,000 users..."
155
+ 10_000.times { |i| adapter.execute(*users.at(i)) }
156
+ puts "Inserting 10,000 exhibits..."
105
157
  10_000.times { |i| adapter.execute(*exhibits.at(i)) }
106
158
 
107
159
  if sqlfile
108
160
  answer = nil
109
161
  until answer && answer[/^$|y|yes|n|no/]
110
- print("Would you like to dump data into tmp/perf.sql (for faster setup)? [Yn]");
162
+ print("Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]");
111
163
  STDOUT.flush
112
164
  answer = gets
113
165
  end
@@ -115,7 +167,7 @@ else
115
167
  if answer[/^$|y|yes/]
116
168
  File.makedirs(File.dirname(sqlfile))
117
169
  #adapter.execute("SELECT * INTO OUTFILE '#{sqlfile}' FROM exhibits;")
118
- `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits > #{sqlfile}`
170
+ `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
119
171
  puts "File saved\n"
120
172
  end
121
173
  end
@@ -127,48 +179,63 @@ TIMES = ENV['x'] ? ENV['x'].to_i : 10_000
127
179
  puts "You can specify how many times you want to run the benchmarks with rake:perf x=(number)"
128
180
  puts "Some tasks will be run 10 and 1000 times less than (number)"
129
181
  puts "Benchmarks will now run #{TIMES} times"
182
+ # Inform about slow benchmark
183
+ # answer = nil
184
+ # until answer && answer[/^$|y|yes|n|no/]
185
+ # print("A slow benchmark exposing problems with SEL is newly added. It takes approx. 20s\n");
186
+ # print("you have scheduled it to run #{TIMES / 100} times.\nWould you still include the particular benchmark? [Yn]")
187
+ # STDOUT.flush
188
+ # answer = gets
189
+ # end
190
+ # run_rel_bench = answer[/^$|y|yes/] ? true : false
191
+
130
192
 
131
193
  RBench.run(TIMES) do
132
194
 
133
195
  column :times
134
- column :dm, :title => "DM 0.9.4"
135
196
  column :ar, :title => "AR 2.1"
136
- column :diff, :compare => [:dm,:ar]
197
+ column :dm, :title => "DM #{DataMapper::VERSION}"
198
+ column :diff, :compare => [:ar,:dm]
137
199
 
138
200
  report "Model.new (instantiation)" do
139
- dm { Exhibit.new }
140
201
  ar { ARExhibit.new }
202
+ dm { Exhibit.new }
141
203
  end
142
204
 
143
205
  report "Model.new (setting attributes)" do
144
206
  attrs = {:name => 'sam', :zoo_id => 1}
145
- dm { Exhibit.new(attrs) }
146
207
  ar { ARExhibit.new(attrs) }
208
+ dm { Exhibit.new(attrs) }
147
209
  end
148
210
 
149
211
  report "Model.get specific (not cached)" do
150
- dm { touch_attributes[Exhibit.get(1)] }
151
212
  ActiveRecord::Base.uncached { ar { touch_attributes[ARExhibit.find(1)] } }
213
+ dm { touch_attributes[Exhibit.get(1)] }
152
214
  end
153
215
 
154
216
  report "Model.get specific (cached)" do
155
- Exhibit.repository(:default) { dm { touch_attributes[Exhibit.get(1)] } }
156
217
  ActiveRecord::Base.cache { ar { touch_attributes[ARExhibit.find(1)] } }
218
+ Exhibit.repository(:default) { dm { touch_attributes[Exhibit.get(1)] } }
157
219
  end
158
220
 
159
221
  report "Model.first" do
160
- dm { touch_attributes[Exhibit.first] }
161
222
  ar { touch_attributes[ARExhibit.first] }
223
+ dm { touch_attributes[Exhibit.first] }
162
224
  end
163
225
 
164
- report "Model.all limit(100)", TIMES / 10 do
165
- dm { touch_attributes[Exhibit.all(:limit => 100)] }
226
+ report "Model.all limit(100)", (TIMES / 10.0).ceil do
166
227
  ar { touch_attributes[ARExhibit.find(:all, :limit => 100)] }
228
+ dm { touch_attributes[Exhibit.all(:limit => 100)] }
167
229
  end
168
230
 
169
- report "Model.all limit(10,000)", TIMES / 1000 do
170
- dm { touch_attributes[Exhibit.all(:limit => 10_000)] }
231
+ report "Model.all limit(100) with relationship", (TIMES / 10.0).ceil do
232
+ ar { touch_relationships[ARExhibit.all(:limit => 100, :include => [:user])] }
233
+ dm { touch_relationships[Exhibit.all(:limit => 100)] }
234
+ end
235
+
236
+ report "Model.all limit(10,000)", (TIMES / 1000.0).ceil do
171
237
  ar { touch_attributes[ARExhibit.find(:all, :limit => 10_000)] }
238
+ dm { touch_attributes[Exhibit.all(:limit => 10_000)] }
172
239
  end
173
240
 
174
241
  create_exhibit = {
@@ -179,25 +246,37 @@ RBench.run(TIMES) do
179
246
  }
180
247
 
181
248
  report "Model.create" do
182
- dm { Exhibit.create(create_exhibit) }
183
249
  ar { ARExhibit.create(create_exhibit) }
250
+ dm { Exhibit.create(create_exhibit) }
251
+ end
252
+
253
+ report "Resource#attributes" do
254
+ attrs_first = {:name => 'sam', :zoo_id => 1}
255
+ attrs_second = {:name => 'tom', :zoo_id => 1}
256
+ ar { e = ARExhibit.new(attrs_first); e.attributes = attrs_second }
257
+ dm { e = Exhibit.new(attrs_first); e.attributes = attrs_second }
184
258
  end
185
259
 
186
260
  report "Resource#update" do
187
- dm { e = Exhibit.get(1); e.name = 'bob'; e.save }
188
- ar { e = ARExhibit.find(1); e.name = 'bob'; e.save }
261
+ ar { e = ARExhibit.find(1); e.name = 'bob'; e.save }
262
+ dm { e = Exhibit.get(1); e.name = 'bob'; e.save }
189
263
  end
190
264
 
191
265
  report "Resource#destroy" do
192
- dm { Exhibit.first.destroy }
193
266
  ar { ARExhibit.first.destroy }
267
+ dm { Exhibit.first.destroy }
194
268
  end
195
269
 
196
- summary "Total"
270
+ report "Model.transaction" do
271
+ ar { ARExhibit.transaction { ARExhibit.new } }
272
+ dm { Exhibit.transaction { Exhibit.new } }
273
+ end
197
274
 
275
+ summary "Total"
198
276
  end
199
277
 
200
278
  connection = adapter.send(:create_connection)
201
279
  command = connection.create_command("DROP TABLE exhibits")
280
+ command = connection.create_command("DROP TABLE users")
202
281
  command.execute_non_query rescue nil
203
282
  connection.close