activerecord 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +34 -0
- data/install.rb +11 -1
- data/lib/active_record/associations.rb +3 -2
- data/lib/active_record/associations/association_collection.rb +7 -3
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +24 -13
- data/lib/active_record/associations/has_many_association.rb +10 -6
- data/lib/active_record/base.rb +62 -29
- data/lib/active_record/connection_adapters/abstract_adapter.rb +3 -3
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/support/binding_of_caller.rb +83 -85
- data/lib/active_record/support/breakpoint.rb +42 -51
- data/lib/active_record/support/core_ext/object_and_class.rb +24 -0
- data/lib/active_record/support/dependencies.rb +20 -15
- data/lib/active_record/timestamp.rb +12 -7
- data/lib/active_record/validations.rb +3 -3
- data/lib/active_record/vendor/mysql411.rb +1 -1
- data/rakefile +1 -1
- data/test/association_inheritance_reload.rb +14 -0
- data/test/associations_test.rb +38 -0
- data/test/base_test.rb +57 -3
- data/test/column_alias_test.rb +12 -0
- data/test/finder_test.rb +32 -1
- data/test/fixtures/company.rb +2 -2
- data/test/fixtures/fixture_database.sqlite +0 -0
- metadata +5 -2
@@ -16,11 +16,14 @@
|
|
16
16
|
# license please contact me.
|
17
17
|
|
18
18
|
require 'irb'
|
19
|
-
|
19
|
+
require File.dirname(__FILE__) + '/binding_of_caller'
|
20
20
|
require 'drb'
|
21
21
|
require 'drb/acl'
|
22
22
|
|
23
23
|
module Breakpoint
|
24
|
+
id = %q$Id: breakpoint.rb 41 2005-01-22 20:22:10Z flgr $
|
25
|
+
Version = id.split(" ")[2].to_i
|
26
|
+
|
24
27
|
extend self
|
25
28
|
|
26
29
|
# This will pop up an interactive ruby session at a
|
@@ -114,10 +117,10 @@ module Breakpoint
|
|
114
117
|
end
|
115
118
|
end
|
116
119
|
|
117
|
-
module CommandBundle
|
120
|
+
module CommandBundle
|
118
121
|
# Proxy to a Breakpoint client. Lets you directly execute code
|
119
122
|
# in the context of the client.
|
120
|
-
class Client
|
123
|
+
class Client
|
121
124
|
def initialize(eval_handler) # :nodoc:
|
122
125
|
@eval_handler = eval_handler
|
123
126
|
end
|
@@ -133,15 +136,23 @@ module Breakpoint
|
|
133
136
|
end
|
134
137
|
|
135
138
|
# Will execute the specified statement at the client.
|
136
|
-
def method_missing(method, *args)
|
137
|
-
if args.empty?
|
138
|
-
result = eval
|
139
|
+
def method_missing(method, *args, &block)
|
140
|
+
if args.empty? and not block
|
141
|
+
result = eval "#{method}"
|
139
142
|
else
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
143
|
+
# This is a bit ugly. The alternative would be using an
|
144
|
+
# eval context instead of an eval handler for executing
|
145
|
+
# the code at the client. The problem with that approach
|
146
|
+
# is that we would have to handle special expressions
|
147
|
+
# like "self", "nil" or constants ourself which is hard.
|
148
|
+
remote = eval %{
|
149
|
+
result = lambda { |block, *args| #{method}(*args, &block) }
|
150
|
+
def result.call_with_block(*args, &block)
|
151
|
+
call(block, *args)
|
152
|
+
end
|
153
|
+
result
|
154
|
+
}
|
155
|
+
remote.call_with_block(*args, &block)
|
145
156
|
end
|
146
157
|
|
147
158
|
return result
|
@@ -175,6 +186,7 @@ module Breakpoint
|
|
175
186
|
# client.File.open("temp.txt", "w") { |f| f.puts "Hello" }
|
176
187
|
def client()
|
177
188
|
if Breakpoint.use_drb? then
|
189
|
+
sleep(0.5) until Breakpoint.drb_service.eval_handler
|
178
190
|
Client.new(Breakpoint.drb_service.eval_handler)
|
179
191
|
else
|
180
192
|
Client.new(lambda { |code| eval(code, TOPLEVEL_BINDING) })
|
@@ -205,7 +217,7 @@ module Breakpoint
|
|
205
217
|
# These exceptions will be raised on failed asserts
|
206
218
|
# if Breakpoint.asserts_cause_exceptions is set to
|
207
219
|
# true.
|
208
|
-
class FailedAssertError < RuntimeError
|
220
|
+
class FailedAssertError < RuntimeError
|
209
221
|
end
|
210
222
|
|
211
223
|
# This asserts that the block evaluates to true.
|
@@ -279,7 +291,7 @@ module Breakpoint
|
|
279
291
|
@collision_handler.call
|
280
292
|
end
|
281
293
|
|
282
|
-
def ping
|
294
|
+
def ping() end
|
283
295
|
|
284
296
|
def add_breakpoint(context, message)
|
285
297
|
workspace = IRB::WorkSpace.new(context)
|
@@ -290,31 +302,7 @@ module Breakpoint
|
|
290
302
|
@handler.call(workspace, message)
|
291
303
|
end
|
292
304
|
|
293
|
-
|
294
|
-
@handler = block
|
295
|
-
end
|
296
|
-
|
297
|
-
def unregister_handler
|
298
|
-
@handler = nil
|
299
|
-
end
|
300
|
-
|
301
|
-
attr_reader :eval_handler
|
302
|
-
|
303
|
-
def register_eval_handler(&block)
|
304
|
-
@eval_handler = block
|
305
|
-
end
|
306
|
-
|
307
|
-
def unregister_eval_handler
|
308
|
-
@eval_handler = lambda { }
|
309
|
-
end
|
310
|
-
|
311
|
-
def register_collision_handler(&block)
|
312
|
-
@collision_handler = block
|
313
|
-
end
|
314
|
-
|
315
|
-
def unregister_collision_handler
|
316
|
-
@collision_handler = lambda { }
|
317
|
-
end
|
305
|
+
attr_accessor :handler, :eval_handler, :collision_handler
|
318
306
|
end
|
319
307
|
|
320
308
|
# Will run Breakpoint in DRb mode. This will spawn a server
|
@@ -359,7 +347,8 @@ module Breakpoint
|
|
359
347
|
#
|
360
348
|
# Detailed information about running DRb through firewalls is
|
361
349
|
# available at http://www.rubygarden.org/ruby?DrbTutorial
|
362
|
-
def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
|
350
|
+
def activate_drb(uri = nil, allowed_hosts = ['localhost', '127.0.0.1', '::1'],
|
351
|
+
ignore_collisions = false)
|
363
352
|
|
364
353
|
return false if @use_drb
|
365
354
|
|
@@ -402,7 +391,7 @@ module Breakpoint
|
|
402
391
|
end
|
403
392
|
|
404
393
|
# Deactivates a running Breakpoint service.
|
405
|
-
def deactivate_drb
|
394
|
+
def deactivate_drb
|
406
395
|
@service.stop_service unless @service.nil?
|
407
396
|
@service = nil
|
408
397
|
@use_drb = false
|
@@ -411,14 +400,12 @@ module Breakpoint
|
|
411
400
|
|
412
401
|
# Returns true when Breakpoints are used over DRb.
|
413
402
|
# Breakpoint.activate_drb causes this to be true.
|
414
|
-
def use_drb?
|
403
|
+
def use_drb?
|
415
404
|
@use_drb == true
|
416
405
|
end
|
417
406
|
end
|
418
407
|
|
419
408
|
module IRB # :nodoc:
|
420
|
-
def IRB.parse_opts() end
|
421
|
-
|
422
409
|
class << self; remove_method :start; end
|
423
410
|
def self.start(ap_path = nil, main_context = nil, workspace = nil)
|
424
411
|
$0 = File::basename(ap_path, ".rb") if ap_path
|
@@ -442,7 +429,11 @@ module IRB # :nodoc:
|
|
442
429
|
@CONF[:MAIN_CONTEXT] = irb.context
|
443
430
|
|
444
431
|
old_sigint = trap("SIGINT") do
|
445
|
-
|
432
|
+
begin
|
433
|
+
irb.signal_handle
|
434
|
+
rescue RubyLex::TerminateLineInput
|
435
|
+
# ignored
|
436
|
+
end
|
446
437
|
end
|
447
438
|
|
448
439
|
catch(:IRB_EXIT) do
|
@@ -466,7 +457,7 @@ module IRB # :nodoc:
|
|
466
457
|
end
|
467
458
|
end
|
468
459
|
|
469
|
-
class Context
|
460
|
+
class Context
|
470
461
|
alias :old_evaluate :evaluate
|
471
462
|
def evaluate(line, line_no)
|
472
463
|
if line.chomp == "exit" then
|
@@ -477,7 +468,7 @@ module IRB # :nodoc:
|
|
477
468
|
end
|
478
469
|
end
|
479
470
|
|
480
|
-
class WorkSpace
|
471
|
+
class WorkSpace
|
481
472
|
alias :old_evaluate :evaluate
|
482
473
|
|
483
474
|
def evaluate(*args)
|
@@ -495,7 +486,7 @@ module IRB # :nodoc:
|
|
495
486
|
end
|
496
487
|
end
|
497
488
|
|
498
|
-
module InputCompletor
|
489
|
+
module InputCompletor
|
499
490
|
def self.eval(code, context, *more)
|
500
491
|
# Big hack, this assumes that InputCompletor
|
501
492
|
# will only call eval() when it wants code
|
@@ -506,9 +497,9 @@ module IRB # :nodoc:
|
|
506
497
|
end
|
507
498
|
|
508
499
|
module DRb # :nodoc:
|
509
|
-
class DRbObject
|
510
|
-
undef :inspect
|
511
|
-
undef :clone
|
500
|
+
class DRbObject
|
501
|
+
undef :inspect if method_defined?(:inspect)
|
502
|
+
undef :clone if method_defined?(:clone)
|
512
503
|
end
|
513
504
|
end
|
514
505
|
|
@@ -524,4 +515,4 @@ def assert(&block)
|
|
524
515
|
Binding.of_caller do |context|
|
525
516
|
Breakpoint.assert(context, &block)
|
526
517
|
end
|
527
|
-
end
|
518
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Object #:nodoc:
|
2
|
+
def remove_subclasses_of(superclass)
|
3
|
+
subclasses_of(superclass).each { |subclass| Object.send(:remove_const, subclass) rescue nil }
|
4
|
+
end
|
5
|
+
|
6
|
+
def subclasses_of(superclass)
|
7
|
+
subclasses = []
|
8
|
+
ObjectSpace.each_object(Class) do |k|
|
9
|
+
next if !k.ancestors.include?(superclass) || superclass == k || k.to_s.include?("::") || subclasses.include?(k.to_s)
|
10
|
+
subclasses << k.to_s
|
11
|
+
end
|
12
|
+
subclasses
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Class #:nodoc:
|
17
|
+
def remove_subclasses
|
18
|
+
Object.remove_subclasses_of(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def subclasses
|
22
|
+
Object.subclasses_of(self)
|
23
|
+
end
|
24
|
+
end
|
@@ -9,14 +9,19 @@ module Dependencies
|
|
9
9
|
@@mechanism = :load
|
10
10
|
mattr_accessor :mechanism
|
11
11
|
|
12
|
+
def load?
|
13
|
+
mechanism == :load
|
14
|
+
end
|
15
|
+
|
12
16
|
def depend_on(file_name, swallow_load_errors = false)
|
13
17
|
if !loaded.include?(file_name)
|
14
18
|
loaded << file_name
|
15
|
-
|
16
19
|
begin
|
17
20
|
require_or_load(file_name)
|
18
21
|
rescue LoadError
|
19
22
|
raise unless swallow_load_errors
|
23
|
+
rescue Object => e
|
24
|
+
raise ScriptError, "#{e.message}"
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
@@ -29,12 +34,16 @@ module Dependencies
|
|
29
34
|
self.loaded = [ ]
|
30
35
|
end
|
31
36
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
37
|
+
def require_or_load(file_name)
|
38
|
+
load? ? load("#{file_name}.rb") : require(file_name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_subclasses_for(*classes)
|
42
|
+
classes.each { |klass| klass.remove_subclasses }
|
43
|
+
end
|
36
44
|
end
|
37
45
|
|
46
|
+
Object.send(:define_method, :require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load)
|
38
47
|
Object.send(:define_method, :require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency)
|
39
48
|
Object.send(:define_method, :require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association)
|
40
49
|
|
@@ -42,16 +51,12 @@ class Object #:nodoc:
|
|
42
51
|
class << self
|
43
52
|
# Use const_missing to autoload associations so we don't have to
|
44
53
|
# require_association when using single-table inheritance.
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
return Object.const_get(class_id) if Object.const_defined?(class_id)
|
52
|
-
rescue LoadError
|
53
|
-
pre_dependency_const_missing(class_id)
|
54
|
-
end
|
54
|
+
def const_missing(class_id)
|
55
|
+
begin
|
56
|
+
require_or_load(class_id.to_s.demodulize.underscore)
|
57
|
+
if Object.const_defined?(class_id) then return Object.const_get(class_id) else raise LoadError end
|
58
|
+
rescue LoadError
|
59
|
+
raise NameError, "uninitialized constant #{class_id}"
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
@@ -4,6 +4,7 @@ module ActiveRecord
|
|
4
4
|
# automatically included, so you don't need to do that manually.
|
5
5
|
#
|
6
6
|
# This behavior can be turned off by setting <tt>ActiveRecord::Base.record_timestamps = false</tt>.
|
7
|
+
# This behavior can use GMT by setting <tt>ActiveRecord::Base.timestamps_gmt = true</tt>
|
7
8
|
module Timestamp
|
8
9
|
def self.append_features(base) # :nodoc:
|
9
10
|
super
|
@@ -18,18 +19,20 @@ module ActiveRecord
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def create_with_timestamps
|
21
|
-
|
22
|
-
write_attribute("
|
22
|
+
t = timestamps_gmt ? Time.now.gmtime : Time.now
|
23
|
+
write_attribute("created_at", t) if record_timestamps && respond_to?(:created_at) && created_at.nil?
|
24
|
+
write_attribute("created_on", t) if record_timestamps && respond_to?(:created_on) && created_on.nil?
|
23
25
|
|
24
|
-
write_attribute("updated_at",
|
25
|
-
write_attribute("updated_on",
|
26
|
+
write_attribute("updated_at", t) if record_timestamps && respond_to?(:updated_at)
|
27
|
+
write_attribute("updated_on", t) if record_timestamps && respond_to?(:updated_on)
|
26
28
|
|
27
29
|
create_without_timestamps
|
28
30
|
end
|
29
31
|
|
30
32
|
def update_with_timestamps
|
31
|
-
|
32
|
-
write_attribute("
|
33
|
+
t = timestamps_gmt ? Time.now.gmtime : Time.now
|
34
|
+
write_attribute("updated_at", t) if record_timestamps && respond_to?(:updated_at)
|
35
|
+
write_attribute("updated_on", t) if record_timestamps && respond_to?(:updated_on)
|
33
36
|
|
34
37
|
update_without_timestamps
|
35
38
|
end
|
@@ -41,5 +44,7 @@ module ActiveRecord
|
|
41
44
|
# if the table has columns of either of these names. This feature is turned on by default.
|
42
45
|
@@record_timestamps = true
|
43
46
|
cattr_accessor :record_timestamps
|
47
|
+
@@timestamps_gmt = false
|
48
|
+
cattr_accessor :timestamps_gmt
|
44
49
|
end
|
45
|
-
end
|
50
|
+
end
|
@@ -254,7 +254,7 @@ module ActiveRecord
|
|
254
254
|
# Configuration options:
|
255
255
|
# * <tt>in</tt> - An enumerable object of available items
|
256
256
|
# * <tt>message</tt> - Specifieds a customer error message (default is: "is not included in the list")
|
257
|
-
# * <tt>
|
257
|
+
# * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
|
258
258
|
def validates_inclusion_of(*attr_names)
|
259
259
|
configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
|
260
260
|
configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
|
@@ -372,9 +372,9 @@ module ActiveRecord
|
|
372
372
|
validations = self.class.read_inheritable_attribute(validation_method.to_s)
|
373
373
|
if validations.nil? then return end
|
374
374
|
validations.each do |validation|
|
375
|
-
if Symbol
|
375
|
+
if validation.is_a?(Symbol)
|
376
376
|
self.send(validation)
|
377
|
-
elsif String
|
377
|
+
elsif validation.is_a?(String)
|
378
378
|
eval(validation, binding)
|
379
379
|
elsif validation_block?(validation)
|
380
380
|
validation.call(self)
|
@@ -255,7 +255,7 @@ class Mysql
|
|
255
255
|
def_value = f[7]
|
256
256
|
max_length = 0
|
257
257
|
|
258
|
-
ret << Field::new(table_name, table_name,
|
258
|
+
ret << Field::new(table_name, table_name, column_name_alias, length, type, flags, decimals, def_value, max_length)
|
259
259
|
end
|
260
260
|
ret
|
261
261
|
end
|
data/rakefile
CHANGED
@@ -8,7 +8,7 @@ require 'rake/contrib/rubyforgepublisher'
|
|
8
8
|
|
9
9
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
10
10
|
PKG_NAME = 'activerecord'
|
11
|
-
PKG_VERSION = '1.
|
11
|
+
PKG_VERSION = '1.6.0' + PKG_BUILD
|
12
12
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
13
13
|
|
14
14
|
PKG_FILES = FileList[
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
require 'fixtures/company'
|
3
|
+
|
4
|
+
class AssociationInheritanceReloadTest < Test::Unit::TestCase
|
5
|
+
fixtures :companies
|
6
|
+
|
7
|
+
def test_set_attributes
|
8
|
+
assert_equal ["errors.add_on_empty('name', \"can't be empty\")"], Firm.read_inheritable_attribute("validate"), "Second run"
|
9
|
+
# ActiveRecord::Base.reset_column_information_and_inheritable_attributes_for_all_subclasses
|
10
|
+
remove_subclass_of(ActiveRecord::Base)
|
11
|
+
load 'fixtures/company.rb'
|
12
|
+
assert_equal ["errors.add_on_empty('name', \"can't be empty\")"], Firm.read_inheritable_attribute("validate"), "Second run"
|
13
|
+
end
|
14
|
+
end
|
data/test/associations_test.rb
CHANGED
@@ -74,6 +74,10 @@ class HasOneAssociationsTest < Test::Unit::TestCase
|
|
74
74
|
assert_equal Account.find(1).credit_limit, @signals37.account.credit_limit
|
75
75
|
end
|
76
76
|
|
77
|
+
def test_triple_equality
|
78
|
+
assert Account === @signals37.account
|
79
|
+
end
|
80
|
+
|
77
81
|
def test_type_mismatch
|
78
82
|
assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.account = 1 }
|
79
83
|
assert_raises(ActiveRecord::AssociationTypeMismatch) { @signals37.account = Project.find(1) }
|
@@ -382,6 +386,14 @@ class HasManyAssociationsTest < Test::Unit::TestCase
|
|
382
386
|
assert_equal 2, @signals37.clients_of_firm(true).size
|
383
387
|
end
|
384
388
|
|
389
|
+
def test_build_many
|
390
|
+
new_clients = @signals37.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}])
|
391
|
+
assert_equal 2, new_clients.size
|
392
|
+
|
393
|
+
assert @signals37.save
|
394
|
+
assert_equal 3, @signals37.clients_of_firm(true).size
|
395
|
+
end
|
396
|
+
|
385
397
|
def test_invalid_build
|
386
398
|
new_client = @signals37.clients_of_firm.build
|
387
399
|
assert new_client.new_record?
|
@@ -399,6 +411,11 @@ class HasManyAssociationsTest < Test::Unit::TestCase
|
|
399
411
|
assert_equal new_client, @signals37.clients_of_firm.last
|
400
412
|
assert_equal new_client, @signals37.clients_of_firm(true).last
|
401
413
|
end
|
414
|
+
|
415
|
+
def test_create_many
|
416
|
+
@signals37.clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
|
417
|
+
assert_equal 3, @signals37.clients_of_firm(true).size
|
418
|
+
end
|
402
419
|
|
403
420
|
def test_deleting
|
404
421
|
force_signal37_to_load_all_clients_of_firm
|
@@ -720,6 +737,27 @@ class HasAndBelongsToManyAssociationsTest < Test::Unit::TestCase
|
|
720
737
|
assert_equal 2, aridridel.projects(true).size
|
721
738
|
end
|
722
739
|
|
740
|
+
def test_habtm_adding_before_save_with_join_attributes
|
741
|
+
no_of_devels = Developer.count
|
742
|
+
no_of_projects = Project.count
|
743
|
+
now = Date.today
|
744
|
+
ken = Developer.new("name" => "Ken")
|
745
|
+
ken.projects.push_with_attributes( Project.find(1), :joined_on => now )
|
746
|
+
p = Project.new("name" => "Foomatic")
|
747
|
+
ken.projects.push_with_attributes( p, :joined_on => now )
|
748
|
+
assert ken.new_record?
|
749
|
+
assert p.new_record?
|
750
|
+
assert ken.save
|
751
|
+
assert !ken.new_record?
|
752
|
+
assert_equal no_of_devels+1, Developer.count
|
753
|
+
assert_equal no_of_projects+1, Project.count
|
754
|
+
assert_equal 2, ken.projects.size
|
755
|
+
assert_equal 2, ken.projects(true).size
|
756
|
+
|
757
|
+
kenReloaded = Developer.find_by_name 'Ken'
|
758
|
+
kenReloaded.projects.each { |prj| assert_equal(now.to_s, prj.joined_on.to_s) }
|
759
|
+
end
|
760
|
+
|
723
761
|
def test_build
|
724
762
|
devel = Developer.find(1)
|
725
763
|
proj = devel.projects.build("name" => "Projekt")
|