sam-dm-core 0.9.6 → 0.9.7

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