datamapper 0.1.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.
- data/CHANGELOG +2 -0
- data/MIT-LICENSE +22 -0
- data/README +1 -0
- data/example.rb +25 -0
- data/lib/data_mapper.rb +30 -0
- data/lib/data_mapper/adapters/abstract_adapter.rb +229 -0
- data/lib/data_mapper/adapters/mysql_adapter.rb +171 -0
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +189 -0
- data/lib/data_mapper/associations.rb +19 -0
- data/lib/data_mapper/associations/belongs_to_association.rb +111 -0
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +100 -0
- data/lib/data_mapper/associations/has_many_association.rb +101 -0
- data/lib/data_mapper/associations/has_one_association.rb +107 -0
- data/lib/data_mapper/base.rb +160 -0
- data/lib/data_mapper/callbacks.rb +47 -0
- data/lib/data_mapper/database.rb +134 -0
- data/lib/data_mapper/extensions/active_record_impersonation.rb +69 -0
- data/lib/data_mapper/extensions/callback_helpers.rb +35 -0
- data/lib/data_mapper/identity_map.rb +21 -0
- data/lib/data_mapper/loaded_set.rb +45 -0
- data/lib/data_mapper/mappings/column.rb +78 -0
- data/lib/data_mapper/mappings/schema.rb +28 -0
- data/lib/data_mapper/mappings/table.rb +99 -0
- data/lib/data_mapper/queries/conditions.rb +141 -0
- data/lib/data_mapper/queries/connection.rb +34 -0
- data/lib/data_mapper/queries/create_table_statement.rb +38 -0
- data/lib/data_mapper/queries/delete_statement.rb +17 -0
- data/lib/data_mapper/queries/drop_table_statement.rb +17 -0
- data/lib/data_mapper/queries/insert_statement.rb +29 -0
- data/lib/data_mapper/queries/reader.rb +42 -0
- data/lib/data_mapper/queries/result.rb +19 -0
- data/lib/data_mapper/queries/select_statement.rb +103 -0
- data/lib/data_mapper/queries/table_exists_statement.rb +17 -0
- data/lib/data_mapper/queries/truncate_table_statement.rb +17 -0
- data/lib/data_mapper/queries/update_statement.rb +25 -0
- data/lib/data_mapper/session.rb +240 -0
- data/lib/data_mapper/support/blank_slate.rb +3 -0
- data/lib/data_mapper/support/connection_pool.rb +117 -0
- data/lib/data_mapper/support/enumerable.rb +27 -0
- data/lib/data_mapper/support/inflector.rb +329 -0
- data/lib/data_mapper/support/proc.rb +69 -0
- data/lib/data_mapper/support/string.rb +23 -0
- data/lib/data_mapper/support/symbol.rb +91 -0
- data/lib/data_mapper/support/weak_hash.rb +46 -0
- data/lib/data_mapper/unit_of_work.rb +38 -0
- data/lib/data_mapper/validations/confirmation_validator.rb +55 -0
- data/lib/data_mapper/validations/contextual_validations.rb +50 -0
- data/lib/data_mapper/validations/format_validator.rb +85 -0
- data/lib/data_mapper/validations/formats/email.rb +78 -0
- data/lib/data_mapper/validations/generic_validator.rb +27 -0
- data/lib/data_mapper/validations/length_validator.rb +75 -0
- data/lib/data_mapper/validations/required_field_validator.rb +47 -0
- data/lib/data_mapper/validations/unique_validator.rb +65 -0
- data/lib/data_mapper/validations/validation_errors.rb +34 -0
- data/lib/data_mapper/validations/validation_helper.rb +60 -0
- data/performance.rb +156 -0
- data/profile_data_mapper.rb +18 -0
- data/rakefile.rb +80 -0
- data/spec/basic_finder.rb +67 -0
- data/spec/belongs_to.rb +47 -0
- data/spec/fixtures/animals.yaml +32 -0
- data/spec/fixtures/exhibits.yaml +90 -0
- data/spec/fixtures/fruit.yaml +6 -0
- data/spec/fixtures/people.yaml +15 -0
- data/spec/fixtures/zoos.yaml +20 -0
- data/spec/has_and_belongs_to_many.rb +25 -0
- data/spec/has_many.rb +34 -0
- data/spec/legacy.rb +14 -0
- data/spec/models/animal.rb +7 -0
- data/spec/models/exhibit.rb +6 -0
- data/spec/models/fruit.rb +6 -0
- data/spec/models/person.rb +7 -0
- data/spec/models/post.rb +4 -0
- data/spec/models/sales_person.rb +4 -0
- data/spec/models/zoo.rb +5 -0
- data/spec/new_record.rb +24 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/sub_select.rb +16 -0
- data/spec/symbolic_operators.rb +21 -0
- data/spec/validates_confirmation_of.rb +36 -0
- data/spec/validates_format_of.rb +61 -0
- data/spec/validates_length_of.rb +101 -0
- data/spec/validates_uniqueness_of.rb +45 -0
- data/spec/validations.rb +63 -0
- metadata +134 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
# http://www.faqs.org/rfcs/rfc2822.html
|
2
|
+
module DataMapper
|
3
|
+
module Validations
|
4
|
+
module Helpers
|
5
|
+
module Email
|
6
|
+
|
7
|
+
module RFC2822
|
8
|
+
EmailAddress = begin
|
9
|
+
alpha = "a-zA-Z"
|
10
|
+
digit = "0-9"
|
11
|
+
atext = "[#{alpha}#{digit}\!\#\$\%\&\'\*+\/\=\?\^\_\`\{\|\}\~\-]"
|
12
|
+
dot_atom_text = "#{atext}+([.]#{atext}*)*"
|
13
|
+
dot_atom = "#{dot_atom_text}"
|
14
|
+
qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]'
|
15
|
+
text = "[\\x01-\\x09\\x11\\x12\\x14-\\x7f]"
|
16
|
+
quoted_pair = "(\\x5c#{text})"
|
17
|
+
qcontent = "(?:#{qtext}|#{quoted_pair})"
|
18
|
+
quoted_string = "[\"]#{qcontent}+[\"]"
|
19
|
+
atom = "#{atext}+"
|
20
|
+
word = "(?:#{atom}|#{quoted_string})"
|
21
|
+
obs_local_part = "#{word}([.]#{word})*"
|
22
|
+
local_part = "(?:#{dot_atom}|#{quoted_string}|#{obs_local_part})"
|
23
|
+
no_ws_ctl = "\\x01-\\x08\\x11\\x12\\x14-\\x1f\\x7f"
|
24
|
+
dtext = "[#{no_ws_ctl}\\x21-\\x5a\\x5e-\\x7e]"
|
25
|
+
dcontent = "(?:#{dtext}|#{quoted_pair})"
|
26
|
+
domain_literal = "\\[#{dcontent}+\\]"
|
27
|
+
obs_domain = "#{atom}([.]#{atom})*"
|
28
|
+
domain = "(?:#{dot_atom}|#{domain_literal}|#{obs_domain})"
|
29
|
+
addr_spec = "#{local_part}\@#{domain}"
|
30
|
+
pattern = /^#{addr_spec}$/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
=begin
|
40
|
+
addresses = [
|
41
|
+
'-- dave --@example.com', # (spaces are invalid unless enclosed in quotation marks)
|
42
|
+
'[dave]@example.com', # (square brackets are invalid, unless contained within quotation marks)
|
43
|
+
'.dave@example.com', # (the local part of a domain name cannot start with a period)
|
44
|
+
'Max@Job 3:14',
|
45
|
+
'Job@Book of Job',
|
46
|
+
'J. P. \'s-Gravezande, a.k.a. The Hacker!@example.com',
|
47
|
+
]
|
48
|
+
addresses.each do |address|
|
49
|
+
if address =~ RFC2822::EmailAddress
|
50
|
+
puts "#{address} deveria ter sido rejeitado, ERRO"
|
51
|
+
else
|
52
|
+
puts "#{address} rejeitado, OK"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
addresses = [
|
58
|
+
'+1~1+@example.com',
|
59
|
+
'{_dave_}@example.com',
|
60
|
+
'"[[ dave ]]"@example.com',
|
61
|
+
'dave."dave"@example.com',
|
62
|
+
'test@localhost',
|
63
|
+
'test@example.com',
|
64
|
+
'test@example.co.uk',
|
65
|
+
'test@example.com.br',
|
66
|
+
'"J. P. \'s-Gravezande, a.k.a. The Hacker!"@example.com',
|
67
|
+
'me@[187.223.45.119]',
|
68
|
+
'someone@123.com',
|
69
|
+
'simon&garfunkel@songs.com'
|
70
|
+
]
|
71
|
+
addresses.each do |address|
|
72
|
+
if address =~ RFC2822::EmailAddress
|
73
|
+
puts "#{address} aceito, OK"
|
74
|
+
else
|
75
|
+
puts "#{address} deveria ser aceito, ERRO"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
=end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
# All Validators should inherit from the GenericValidator.
|
5
|
+
class GenericValidator
|
6
|
+
|
7
|
+
# Adds an error message to the target class.
|
8
|
+
def add_error(target, message, attribute = :base)
|
9
|
+
target.errors.add(attribute, message)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Gets the proper error message
|
13
|
+
def validation_error_message(default, custom_message, validation_binding)
|
14
|
+
eval("\"#{(custom_message || default)}\"", validation_binding)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Call the validator. We use "call" so the operation
|
18
|
+
# is BoundMethod and Block compatible.
|
19
|
+
# The result should always be TRUE or FALSE.
|
20
|
+
def call(target)
|
21
|
+
raise 'You must overwrite this method'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
class LengthValidator < GenericValidator
|
5
|
+
|
6
|
+
ERROR_MESSAGES = {
|
7
|
+
:range => '#{field} must be between #{min} and #{max} characters long',
|
8
|
+
:min => '#{field} must be more than #{min} characters long',
|
9
|
+
:max => '#{field} must be less than #{max} characters long',
|
10
|
+
:equals => '#{field} must be #{equal} characters long'
|
11
|
+
}
|
12
|
+
|
13
|
+
def initialize(field_name, options)
|
14
|
+
@field_name = field_name
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
@min = options[:minimum] || options[:min]
|
18
|
+
@max = options[:maximum] || options[:max]
|
19
|
+
@equal = options[:is] || options[:equals]
|
20
|
+
@range = options[:within] || options[:in]
|
21
|
+
|
22
|
+
@validation_method ||= :range if @range
|
23
|
+
@validation_method ||= :min if @min && @max.nil?
|
24
|
+
@validation_method ||= :max if @max && @min.nil?
|
25
|
+
@validation_method ||= :equals unless @equal.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def call(target)
|
29
|
+
field_value = target.instance_variable_get("@#{@field_name}").to_s
|
30
|
+
return true if @options[:allow_nil] && field_value.nil?
|
31
|
+
|
32
|
+
# HACK seems hacky to do this on every validation, probably should do this elsewhere?
|
33
|
+
field = Inflector.humanize(@field_name)
|
34
|
+
min = @range ? @range.min : @min
|
35
|
+
max = @range ? @range.max : @max
|
36
|
+
equal = @equal
|
37
|
+
|
38
|
+
error_message = validation_error_message(ERROR_MESSAGES[@validation_method], nil, binding)
|
39
|
+
|
40
|
+
valid = case @validation_method
|
41
|
+
when :range then
|
42
|
+
@range.include?(field_value.size)
|
43
|
+
when :min then
|
44
|
+
field_value.size >= min
|
45
|
+
when :max then
|
46
|
+
field_value.size <= max
|
47
|
+
when :equals then
|
48
|
+
field_value.size == equal
|
49
|
+
end
|
50
|
+
|
51
|
+
add_error(target, error_message, @field_name) unless valid
|
52
|
+
|
53
|
+
return valid
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
module ValidatesLengthOf
|
59
|
+
def self.included(base)
|
60
|
+
base.extend(ClassMethods)
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
DEFAULT_VALIDATES_LENGTH_OF_OPTIONS = { :on => :save }
|
65
|
+
|
66
|
+
def validates_length_of(field, options = {})
|
67
|
+
opts = retrieve_options_from_arguments_for_validators([options], DEFAULT_VALIDATES_LENGTH_OF_OPTIONS)
|
68
|
+
validations.context(opts[:context]) << Validations::LengthValidator.new(field, opts)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
class RequiredFieldValidator < GenericValidator
|
5
|
+
|
6
|
+
ERROR_MESSAGES = {
|
7
|
+
:required => '#{field} must not be blank'
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(field_name)
|
11
|
+
@field_name = field_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(target)
|
15
|
+
field_value = !target.instance_variable_get("@#{@field_name}").nil?
|
16
|
+
return true if field_value
|
17
|
+
|
18
|
+
field = Inflector.humanize(@field_name)
|
19
|
+
|
20
|
+
error_message = validation_error_message(ERROR_MESSAGES[:required], nil, binding)
|
21
|
+
add_error(target, error_message , @field_name)
|
22
|
+
|
23
|
+
return false
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module ValidatesPresenceOf
|
29
|
+
def self.included(base)
|
30
|
+
base.extend(ClassMethods)
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
|
35
|
+
def validates_presence_of(*fields)
|
36
|
+
options = retrieve_options_from_arguments_for_validators(fields)
|
37
|
+
|
38
|
+
fields.each do |field|
|
39
|
+
validations.context(options[:context]) << Validations::RequiredFieldValidator.new(field)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
class UniqueValidator < GenericValidator
|
5
|
+
|
6
|
+
ERROR_MESSAGES = {
|
7
|
+
:unique => '#{field} has already been taken'
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(field_name, options = {})
|
11
|
+
@options = options
|
12
|
+
@field_name = field_name.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(target)
|
16
|
+
field = Inflector.humanize(@field_name)
|
17
|
+
|
18
|
+
unless valid?(target)
|
19
|
+
error_message = validation_error_message(ERROR_MESSAGES[:unique], nil, binding)
|
20
|
+
add_error(target, error_message , @field_name)
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
|
27
|
+
def valid?(target)
|
28
|
+
field_value = target.instance_variable_get("@#{@field_name}")
|
29
|
+
return true if @options[:allow_nil] && field_value.nil?
|
30
|
+
|
31
|
+
finder_options = { @field_name => field_value }
|
32
|
+
|
33
|
+
if @options[:scope]
|
34
|
+
scope_value = target.instance_variable_get("@#{@options[:scope]}")
|
35
|
+
finder_options.merge! @options[:scope] => scope_value
|
36
|
+
end
|
37
|
+
|
38
|
+
finder_options.merge!({ target.session.mappings[target.class].key.name.not => target.key }) unless target.new_record?
|
39
|
+
|
40
|
+
# HACK: gotta make sure we're using the same database that this instance was
|
41
|
+
# found with unless new_record?
|
42
|
+
target.session.find(target.class, :first, finder_options).nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
module ValidatesUniquenessOf
|
48
|
+
def self.included(base)
|
49
|
+
base.extend(ClassMethods)
|
50
|
+
end
|
51
|
+
|
52
|
+
module ClassMethods
|
53
|
+
# No bueno?
|
54
|
+
DEFAULT_OPTIONS = { :on => :save }
|
55
|
+
|
56
|
+
def validates_uniqueness_of(field, options = {})
|
57
|
+
opts = retrieve_options_from_arguments_for_validators([options], DEFAULT_OPTIONS)
|
58
|
+
validations.context(opts[:context]) << Validations::UniqueValidator.new(field, opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module DataMapper
|
2
|
+
|
3
|
+
class ValidationErrors
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@errors = Hash.new { |h,k| h[k.to_sym] = [] }
|
7
|
+
end
|
8
|
+
|
9
|
+
# Clear existing validation errors.
|
10
|
+
def clear!
|
11
|
+
@errors.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
# Add a validation error. Use the attribute :general if
|
15
|
+
# the error doesn't apply to a specific attribute.
|
16
|
+
def add(attribute, message)
|
17
|
+
@errors[attribute] << message
|
18
|
+
end
|
19
|
+
|
20
|
+
# Collect all errors into a single list.
|
21
|
+
def full_messages
|
22
|
+
@errors.inject([]) do |list,pair|
|
23
|
+
list += pair.last
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Are any errors present?
|
28
|
+
def empty?
|
29
|
+
@errors.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
dirname = File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require dirname + '/validation_errors'
|
4
|
+
require dirname + '/contextual_validations'
|
5
|
+
require dirname + '/generic_validator'
|
6
|
+
|
7
|
+
Dir[dirname + '/*_validator.rb'].reject do |path|
|
8
|
+
path =~ /\/generic_validator/
|
9
|
+
end.each do |validator|
|
10
|
+
load validator
|
11
|
+
end
|
12
|
+
|
13
|
+
module DataMapper
|
14
|
+
module Extensions
|
15
|
+
|
16
|
+
module ValidationHelper
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
base.class_eval do
|
21
|
+
include DataMapper::Validations::ValidatesPresenceOf
|
22
|
+
include DataMapper::Validations::ValidatesLengthOf
|
23
|
+
include DataMapper::Validations::ValidatesConfirmationOf
|
24
|
+
include DataMapper::Validations::ValidatesUniquenessOf
|
25
|
+
include DataMapper::Validations::ValidatesFormatOf
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def errors
|
30
|
+
@errors ||= ValidationErrors.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def valid?(context = :general)
|
34
|
+
self.class.validations.execute(context, self)
|
35
|
+
end
|
36
|
+
|
37
|
+
module ClassMethods
|
38
|
+
|
39
|
+
def validations
|
40
|
+
@validations ||= ContextualValidations.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def retrieve_options_from_arguments_for_validators(args, defaults = nil)
|
44
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
45
|
+
|
46
|
+
context = :general
|
47
|
+
context = options[:context] if options.has_key?(:context)
|
48
|
+
context = options.delete(:on) if options.has_key?(:on)
|
49
|
+
options[:context] = context
|
50
|
+
|
51
|
+
options.merge!(defaults) unless defaults.nil?
|
52
|
+
return options
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
data/performance.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection :adapter => 'mysql',
|
4
|
+
:host => 'localhost',
|
5
|
+
:username => 'root',
|
6
|
+
:password => '',
|
7
|
+
:database => 'data_mapper_1'
|
8
|
+
|
9
|
+
class ARAnimal < ActiveRecord::Base
|
10
|
+
set_table_name 'animals'
|
11
|
+
end
|
12
|
+
|
13
|
+
class ARPerson < ActiveRecord::Base
|
14
|
+
set_table_name 'people'
|
15
|
+
end
|
16
|
+
|
17
|
+
$LOAD_PATH.unshift('lib')
|
18
|
+
require 'data_mapper'
|
19
|
+
|
20
|
+
log_path = File.dirname(__FILE__) + '/development.log'
|
21
|
+
|
22
|
+
require 'fileutils'
|
23
|
+
FileUtils::rm log_path if File.exists?(log_path)
|
24
|
+
|
25
|
+
DataMapper::Database.setup do
|
26
|
+
adapter 'mysql'
|
27
|
+
username 'root'
|
28
|
+
database 'data_mapper_1'
|
29
|
+
log_stream 'development.log'
|
30
|
+
log_level Logger::DEBUG
|
31
|
+
end
|
32
|
+
|
33
|
+
class DMAnimal < DataMapper::Base
|
34
|
+
set_table_name 'animals'
|
35
|
+
property :name, :string
|
36
|
+
property :notes, :string, :lazy => true
|
37
|
+
end
|
38
|
+
|
39
|
+
class DMPerson < DataMapper::Base
|
40
|
+
set_table_name 'people'
|
41
|
+
property :name, :string
|
42
|
+
property :age, :integer
|
43
|
+
property :occupation, :string
|
44
|
+
property :notes, :text, :lazy => true
|
45
|
+
end
|
46
|
+
|
47
|
+
class Exhibit < DataMapper::Base
|
48
|
+
property :name, :string
|
49
|
+
belongs_to :zoo
|
50
|
+
end
|
51
|
+
|
52
|
+
class Zoo < DataMapper::Base
|
53
|
+
property :name, :string
|
54
|
+
has_many :exhibits
|
55
|
+
end
|
56
|
+
|
57
|
+
class ARZoo < ActiveRecord::Base
|
58
|
+
set_table_name 'zoos'
|
59
|
+
has_many :exhibits, :class_name => 'ARExhibit', :foreign_key => 'zoo_id'
|
60
|
+
end
|
61
|
+
|
62
|
+
class ARExhibit < ActiveRecord::Base
|
63
|
+
set_table_name 'exhibits'
|
64
|
+
belongs_to :zoo, :class_name => 'ARZoo', :foreign_key => 'zoo_id'
|
65
|
+
end
|
66
|
+
|
67
|
+
N = (ENV['N'] || 1000).to_i
|
68
|
+
|
69
|
+
Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
70
|
+
|
71
|
+
x.report('ActiveRecord:id') do
|
72
|
+
N.times { ARAnimal.find(1) }
|
73
|
+
end
|
74
|
+
|
75
|
+
x.report('DataMapper:id') do
|
76
|
+
N.times { DMAnimal[1] }
|
77
|
+
end
|
78
|
+
|
79
|
+
x.report('ActiveRecord:conditions') do
|
80
|
+
N.times { ARZoo.find(:first, :conditions => ['name = ?', 'Galveston']) }
|
81
|
+
end
|
82
|
+
|
83
|
+
x.report('DataMapper:conditions:short') do
|
84
|
+
N.times { Zoo[:name => 'Galveston'] }
|
85
|
+
end
|
86
|
+
|
87
|
+
x.report('DataMapper:conditions:long') do
|
88
|
+
N.times { Zoo.find(:first, :conditions => ['name = ?', 'Galveston']) }
|
89
|
+
end
|
90
|
+
|
91
|
+
people = [
|
92
|
+
['Sam', 29, 'Programmer'],
|
93
|
+
['Amy', 28, 'Business Analyst Manager'],
|
94
|
+
['Scott', 25, 'Programmer'],
|
95
|
+
['Josh', 23, 'Supervisor'],
|
96
|
+
['Bob', 40, 'Peon']
|
97
|
+
]
|
98
|
+
|
99
|
+
DMPerson.truncate!
|
100
|
+
|
101
|
+
x.report('ActiveRecord:insert') do
|
102
|
+
N.times do
|
103
|
+
people.each do |a|
|
104
|
+
ARPerson::create(:name => a[0], :age => a[1], :occupation => a[2])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
DMPerson.truncate!
|
110
|
+
|
111
|
+
x.report('DataMapper:insert') do
|
112
|
+
N.times do
|
113
|
+
people.each do |a|
|
114
|
+
DMPerson::create(:name => a[0], :age => a[1], :occupation => a[2])
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
x.report('ActiveRecord:update') do
|
120
|
+
N.times do
|
121
|
+
bob = ARAnimal.find(:first, :conditions => ["name = ?", 'elephant'])
|
122
|
+
bob.notes = 'Updated by ActiveRecord'
|
123
|
+
bob.save
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
x.report('DataMapper:update') do
|
128
|
+
N.times do
|
129
|
+
bob = DMAnimal.first(:name => 'elephant')
|
130
|
+
bob.notes = 'Updated by DataMapper'
|
131
|
+
bob.save
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
x.report('ActiveRecord:associations:lazy') do
|
136
|
+
N.times do
|
137
|
+
zoos = ARZoo.find(:all)
|
138
|
+
zoos.each { |zoo| zoo.exhibits.entries }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
x.report('ActiveRecord:associations:included') do
|
143
|
+
N.times do
|
144
|
+
zoos = ARZoo.find(:all, :include => [:exhibits])
|
145
|
+
zoos.each { |zoo| zoo.exhibits.entries }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
x.report('DataMapper:associations') do
|
150
|
+
N.times do
|
151
|
+
database do
|
152
|
+
Zoo.all.each { |zoo| zoo.exhibits.entries }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|