activerecord 1.4.0 → 1.5.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 +98 -0
- data/install.rb +1 -0
- data/lib/active_record.rb +1 -0
- data/lib/active_record/acts/list.rb +19 -16
- data/lib/active_record/associations.rb +164 -164
- data/lib/active_record/associations/association_collection.rb +44 -71
- data/lib/active_record/associations/association_proxy.rb +76 -0
- data/lib/active_record/associations/belongs_to_association.rb +74 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +34 -21
- data/lib/active_record/associations/has_many_association.rb +34 -30
- data/lib/active_record/associations/has_one_association.rb +48 -0
- data/lib/active_record/base.rb +62 -18
- data/lib/active_record/callbacks.rb +17 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +11 -10
- data/lib/active_record/connection_adapters/mysql_adapter.rb +1 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -1
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +94 -73
- data/lib/active_record/deprecated_associations.rb +46 -8
- data/lib/active_record/fixtures.rb +1 -1
- data/lib/active_record/observer.rb +5 -1
- data/lib/active_record/support/binding_of_caller.rb +72 -68
- data/lib/active_record/support/breakpoint.rb +526 -524
- data/lib/active_record/support/class_inheritable_attributes.rb +105 -29
- data/lib/active_record/support/core_ext.rb +1 -0
- data/lib/active_record/support/core_ext/hash.rb +5 -0
- data/lib/active_record/support/core_ext/hash/keys.rb +35 -0
- data/lib/active_record/support/core_ext/numeric.rb +7 -0
- data/lib/active_record/support/core_ext/numeric/bytes.rb +33 -0
- data/lib/active_record/support/core_ext/numeric/time.rb +59 -0
- data/lib/active_record/support/core_ext/string.rb +5 -0
- data/lib/active_record/support/core_ext/string/inflections.rb +41 -0
- data/lib/active_record/support/dependencies.rb +1 -14
- data/lib/active_record/support/inflector.rb +6 -6
- data/lib/active_record/support/misc.rb +0 -24
- data/lib/active_record/validations.rb +34 -1
- data/lib/active_record/vendor/mysql411.rb +305 -0
- data/rakefile +11 -2
- data/test/abstract_unit.rb +1 -2
- data/test/associations_test.rb +234 -23
- data/test/base_test.rb +50 -1
- data/test/callbacks_test.rb +16 -0
- data/test/connections/native_mysql/connection.rb +2 -2
- data/test/connections/native_sqlite3/connection.rb +34 -0
- data/test/deprecated_associations_test.rb +36 -2
- data/test/fixtures/company.rb +2 -0
- data/test/fixtures/computer.rb +3 -0
- data/test/fixtures/computers.yml +3 -0
- data/test/fixtures/db_definitions/db2.sql +5 -0
- data/test/fixtures/db_definitions/mysql.sql +5 -0
- data/test/fixtures/db_definitions/postgresql.sql +5 -0
- data/test/fixtures/db_definitions/sqlite.sql +5 -0
- data/test/fixtures/db_definitions/sqlserver.sql +5 -1
- data/test/fixtures/fixture_database.sqlite +0 -0
- data/test/validations_test.rb +21 -0
- metadata +22 -2
@@ -1,33 +1,67 @@
|
|
1
1
|
# sqlite_adapter.rb
|
2
|
-
# author:
|
2
|
+
# author: Luke Holden <lholden@cablelan.net>
|
3
|
+
# updated for SQLite3: Jamis Buck <jamis_buck@byu.edu>
|
3
4
|
|
4
5
|
require 'active_record/connection_adapters/abstract_adapter'
|
5
6
|
|
6
7
|
module ActiveRecord
|
7
8
|
class Base
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
class << self
|
10
|
+
# sqlite3 adapter reuses sqlite_connection.
|
11
|
+
def sqlite3_connection(config) # :nodoc:
|
12
|
+
parse_config!(config)
|
13
|
+
|
14
|
+
unless self.class.const_defined?(:SQLite3)
|
15
|
+
require_library_or_gem(config[:adapter])
|
16
|
+
end
|
17
|
+
|
18
|
+
db = SQLite3::Database.new(
|
19
|
+
config[:dbfile],
|
20
|
+
:results_as_hash => true,
|
21
|
+
:type_translation => false
|
22
|
+
)
|
23
|
+
ConnectionAdapters::SQLiteAdapter.new(db, logger)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Establishes a connection to the database that's used by all Active Record objects
|
27
|
+
def sqlite_connection(config) # :nodoc:
|
28
|
+
parse_config!(config)
|
29
|
+
|
30
|
+
unless self.class.const_defined?(:SQLite)
|
31
|
+
require_library_or_gem(config[:adapter])
|
32
|
+
|
33
|
+
db = SQLite::Database.new(config[:dbfile], 0)
|
34
|
+
db.show_datatypes = "ON" if !defined? SQLite::Version
|
35
|
+
db.results_as_hash = true if defined? SQLite::Version
|
36
|
+
db.type_translation = false
|
37
|
+
|
38
|
+
# "Downgrade" deprecated sqlite API
|
39
|
+
if SQLite.const_defined?(:Version)
|
40
|
+
ConnectionAdapters::SQLiteAdapter.new(db, logger)
|
41
|
+
else
|
42
|
+
ConnectionAdapters::DeprecatedSQLiteAdapter.new(db, logger)
|
43
|
+
end
|
44
|
+
end
|
14
45
|
end
|
15
|
-
|
16
|
-
config[:dbfile] = File.expand_path(config[:dbfile], RAILS_ROOT) if Object.const_defined?(:RAILS_ROOT)
|
17
|
-
db = SQLite::Database.new(config[:dbfile], 0)
|
18
46
|
|
19
|
-
|
20
|
-
|
21
|
-
|
47
|
+
private
|
48
|
+
def parse_config!(config)
|
49
|
+
# Require dbfile.
|
50
|
+
unless config.has_key?(:dbfile)
|
51
|
+
raise ArgumentError, "No database file specified. Missing argument: dbfile"
|
52
|
+
end
|
22
53
|
|
23
|
-
|
54
|
+
# Allow database path relative to RAILS_ROOT.
|
55
|
+
if Object.const_defined?(:RAILS_ROOT)
|
56
|
+
config[:dbfile] = File.expand_path(config[:dbfile], RAILS_ROOT)
|
57
|
+
end
|
58
|
+
end
|
24
59
|
end
|
25
60
|
end
|
26
61
|
|
27
62
|
module ConnectionAdapters
|
28
63
|
|
29
64
|
class SQLiteColumn < Column
|
30
|
-
|
31
65
|
def string_to_binary(value)
|
32
66
|
value.gsub(/(\0|\%)/) do
|
33
67
|
case $1
|
@@ -45,92 +79,79 @@ module ActiveRecord
|
|
45
79
|
end
|
46
80
|
end
|
47
81
|
end
|
48
|
-
|
49
82
|
end
|
83
|
+
|
50
84
|
class SQLiteAdapter < AbstractAdapter # :nodoc:
|
51
|
-
def
|
52
|
-
|
85
|
+
def execute(sql, name = nil)
|
86
|
+
log(sql, name) { @connection.execute(sql) }
|
53
87
|
end
|
54
88
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
89
|
+
def update(sql, name = nil)
|
90
|
+
execute(sql, name)
|
91
|
+
@connection.changes
|
58
92
|
end
|
59
93
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
94
|
+
def delete(sql, name = nil)
|
95
|
+
sql += " WHERE 1=1" unless sql =~ /WHERE/i
|
96
|
+
execute(sql, name)
|
97
|
+
@connection.changes
|
65
98
|
end
|
66
99
|
|
67
100
|
def insert(sql, name = nil, pk = nil, id_value = nil)
|
68
101
|
execute(sql, name = nil)
|
69
|
-
id_value || @connection.
|
102
|
+
id_value || @connection.last_insert_row_id
|
70
103
|
end
|
71
104
|
|
72
|
-
def
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
when "COMMIT" then connection.commit
|
78
|
-
when "ROLLBACK" then connection.rollback
|
79
|
-
else connection.execute(sql)
|
80
|
-
end
|
81
|
-
else
|
82
|
-
connection.execute( sql )
|
105
|
+
def select_all(sql, name = nil)
|
106
|
+
execute(sql, name).map do |row|
|
107
|
+
record = {}
|
108
|
+
row.each_key do |key|
|
109
|
+
record[key.sub(/\w+\./, '')] = row[key] unless key.is_a?(Fixnum)
|
83
110
|
end
|
111
|
+
record
|
84
112
|
end
|
85
113
|
end
|
86
114
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
115
|
+
def select_one(sql, name = nil)
|
116
|
+
result = select_all(sql, name)
|
117
|
+
result.nil? ? nil : result.first
|
90
118
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
119
|
+
|
120
|
+
|
121
|
+
def begin_db_transaction() @connection.transaction end
|
122
|
+
def commit_db_transaction() @connection.commit end
|
123
|
+
def rollback_db_transaction() @connection.rollback end
|
124
|
+
|
125
|
+
|
126
|
+
def tables
|
127
|
+
execute('.table').map { |table| Table.new(table) }
|
96
128
|
end
|
97
129
|
|
98
|
-
def
|
99
|
-
|
100
|
-
|
130
|
+
def columns(table_name, name = nil)
|
131
|
+
table_structure(table_name).map { |field|
|
132
|
+
SQLiteColumn.new(field['name'], field['dflt_value'], field['type'])
|
133
|
+
}
|
134
|
+
end
|
101
135
|
|
102
136
|
def quote_string(s)
|
103
|
-
|
137
|
+
@connection.class.quote(s)
|
104
138
|
end
|
105
|
-
|
139
|
+
|
106
140
|
def quote_column_name(name)
|
107
141
|
return "'#{name}'"
|
108
142
|
end
|
109
143
|
|
110
|
-
|
111
|
-
def select(sql, name = nil)
|
112
|
-
results = nil
|
113
|
-
log(sql, name, @connection) { |connection| results = connection.execute(sql) }
|
114
|
-
|
115
|
-
rows = []
|
116
|
-
|
117
|
-
results.each do |row|
|
118
|
-
hash_only_row = {}
|
119
|
-
row.each_key do |key|
|
120
|
-
hash_only_row[key.sub(/\w+\./, "")] = row[key] unless key.class == Fixnum
|
121
|
-
end
|
122
|
-
rows << hash_only_row
|
123
|
-
end
|
124
|
-
|
125
|
-
return rows
|
126
|
-
end
|
127
|
-
|
144
|
+
protected
|
128
145
|
def table_structure(table_name)
|
129
|
-
|
130
|
-
results = nil
|
131
|
-
log(sql, nil, @connection) { |connection| results = connection.execute(sql) }
|
132
|
-
return results
|
146
|
+
execute "PRAGMA table_info(#{table_name})"
|
133
147
|
end
|
134
148
|
end
|
149
|
+
|
150
|
+
class DeprecatedSQLiteAdapter < SQLiteAdapter # :nodoc:
|
151
|
+
def insert(sql, name = nil, pk = nil, id_value = nil)
|
152
|
+
execute(sql, name = nil)
|
153
|
+
id_value || @connection.last_insert_rowid
|
154
|
+
end
|
155
|
+
end
|
135
156
|
end
|
136
157
|
end
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
end_eval
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
def deprecated_remove_association_relation(association_name)# :nodoc:
|
22
22
|
module_eval <<-"end_eval", __FILE__, __LINE__
|
23
23
|
def remove_#{association_name}(*items)
|
24
24
|
#{association_name}.delete(items)
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
end_eval
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
53
|
+
def deprecated_collection_create_method(collection_name)# :nodoc:
|
54
54
|
module_eval <<-"end_eval", __FILE__, __LINE__
|
55
55
|
def create_in_#{collection_name}(attributes = {})
|
56
56
|
#{collection_name}.create(attributes)
|
@@ -58,13 +58,51 @@ module ActiveRecord
|
|
58
58
|
end_eval
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
def deprecated_collection_build_method(collection_name)# :nodoc:
|
62
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
63
|
+
def build_to_#{collection_name}(attributes = {})
|
64
|
+
#{collection_name}.build(attributes)
|
65
|
+
end
|
66
|
+
end_eval
|
67
|
+
end
|
68
|
+
|
69
|
+
def deprecated_association_comparison_method(association_name, association_class_name)
|
70
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
71
|
+
def #{association_name}?(comparison_object, force_reload = false)
|
72
|
+
if comparison_object.kind_of?(#{association_class_name})
|
73
|
+
#{association_name}(force_reload) == comparison_object
|
74
|
+
else
|
75
|
+
raise "Comparison object is a #{association_class_name}, should have been \#{comparison_object.class.name}"
|
65
76
|
end
|
66
|
-
|
67
|
-
|
77
|
+
end
|
78
|
+
end_eval
|
79
|
+
end
|
80
|
+
|
81
|
+
def deprecated_has_association_method(association_name)
|
82
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
83
|
+
def has_#{association_name}?(force_reload = false)
|
84
|
+
!#{association_name}(force_reload).nil?
|
85
|
+
end
|
86
|
+
end_eval
|
87
|
+
end
|
88
|
+
|
89
|
+
def deprecated_build_method(method_prefix, collection_name, collection_class_name, class_primary_key_name)
|
90
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
91
|
+
def #{method_prefix + collection_name}(attributes = {})
|
92
|
+
association = #{collection_class_name}.new
|
93
|
+
association.attributes = attributes.merge({ "#{class_primary_key_name}" => id})
|
94
|
+
association
|
95
|
+
end
|
96
|
+
end_eval
|
97
|
+
end
|
98
|
+
|
99
|
+
def deprecated_create_method(method_prefix, collection_name, collection_class_name, class_primary_key_name)
|
100
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
101
|
+
def #{method_prefix + collection_name}(attributes = nil)
|
102
|
+
#{collection_class_name}.create((attributes || {}).merge({ "#{class_primary_key_name}" => id}))
|
103
|
+
end
|
104
|
+
end_eval
|
105
|
+
end
|
68
106
|
end
|
69
107
|
end
|
70
108
|
end
|
@@ -158,7 +158,7 @@ class Fixtures < Hash
|
|
158
158
|
ActiveRecord::Base.logger.level = Logger::ERROR
|
159
159
|
|
160
160
|
fixtures = table_names.flatten.map do |table_name|
|
161
|
-
Fixtures.new(connection, table_name.to_s, File.join(fixtures_directory, table_name.to_s))
|
161
|
+
Fixtures.new(connection, File.split(table_name.to_s).last, File.join(fixtures_directory, table_name.to_s))
|
162
162
|
end
|
163
163
|
|
164
164
|
connection.transaction do
|
@@ -43,7 +43,11 @@ module ActiveRecord
|
|
43
43
|
# The observer can implement callback methods for each of the methods described in the Callbacks module.
|
44
44
|
class Observer
|
45
45
|
include Singleton
|
46
|
-
|
46
|
+
|
47
|
+
def self.observe(*models)
|
48
|
+
define_method(:observed_class) { models }
|
49
|
+
end
|
50
|
+
|
47
51
|
def initialize
|
48
52
|
[ observed_class ].flatten.each do |klass|
|
49
53
|
klass.add_observer(self)
|
@@ -1,81 +1,85 @@
|
|
1
1
|
begin
|
2
2
|
require 'simplecc'
|
3
3
|
rescue LoadError
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Continuation #:nodoc:
|
5
|
+
def create(*args, &block)
|
6
|
+
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
|
7
|
+
result ||= args
|
8
|
+
return *[cc, *result]
|
9
|
+
end
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
# method
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# #
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
13
|
+
class Binding #:nodoc:
|
14
|
+
# This method returns the binding of the method that called your
|
15
|
+
# method. It will raise an Exception when you're not inside a method.
|
16
|
+
#
|
17
|
+
# It's used like this:
|
18
|
+
# def inc_counter(amount = 1)
|
19
|
+
# Binding.of_caller do |binding|
|
20
|
+
# # Create a lambda that will increase the variable 'counter'
|
21
|
+
# # in the caller of this method when called.
|
22
|
+
# inc = eval("lambda { |arg| counter += arg }", binding)
|
23
|
+
# # We can refer to amount from inside this block safely.
|
24
|
+
# inc.call(amount)
|
25
|
+
# end
|
26
|
+
# # No other statements can go here. Put them inside the block.
|
27
|
+
# end
|
28
|
+
# counter = 0
|
29
|
+
# 2.times { inc_counter }
|
30
|
+
# counter # => 2
|
31
|
+
#
|
32
|
+
# Binding.of_caller must be the last statement in the method.
|
33
|
+
# This means that you will have to put everything you want to
|
34
|
+
# do after the call to Binding.of_caller into the block of it.
|
35
|
+
# This should be no problem however, because Ruby has closures.
|
36
|
+
# If you don't do this an Exception will be raised. Because of
|
37
|
+
# the way that Binding.of_caller is implemented it has to be
|
38
|
+
# done this way.
|
39
|
+
def of_caller(&block)
|
40
|
+
old_critical = Thread.critical
|
41
|
+
Thread.critical = true
|
42
|
+
count = 0
|
43
|
+
cc, result, error, extra_data = Continuation.create(nil, nil)
|
44
|
+
error.call if error
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
46
|
+
tracer = lambda do |*args|
|
47
|
+
type, context, extra_data = args[0], args[4], args
|
48
|
+
if type == "return"
|
49
|
+
count += 1
|
50
|
+
# First this method and then calling one will return --
|
51
|
+
# the trace event of the second event gets the context
|
52
|
+
# of the method which called the method that called this
|
53
|
+
# method.
|
54
|
+
if count == 2
|
55
|
+
# It would be nice if we could restore the trace_func
|
56
|
+
# that was set before we swapped in our own one, but
|
57
|
+
# this is impossible without overloading set_trace_func
|
58
|
+
# in current Ruby.
|
59
|
+
set_trace_func(nil)
|
60
|
+
cc.call(eval("binding", context), nil, extra_data)
|
61
|
+
end
|
62
|
+
elsif type == "line" then
|
63
|
+
nil
|
64
|
+
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
65
|
+
nil
|
66
|
+
else
|
56
67
|
set_trace_func(nil)
|
57
|
-
|
68
|
+
error_msg = "Binding.of_caller used in non-method context or " +
|
69
|
+
"trailing statements of method using it aren't in the block."
|
70
|
+
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
58
71
|
end
|
59
|
-
elsif type == "line" then
|
60
|
-
nil
|
61
|
-
elsif type == "c-return" and extra_data[3] == :set_trace_func then
|
62
|
-
nil
|
63
|
-
else
|
64
|
-
set_trace_func(nil)
|
65
|
-
error_msg = "Binding.of_caller used in non-method context or " +
|
66
|
-
"trailing statements of method using it aren't in the block."
|
67
|
-
cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil)
|
68
72
|
end
|
69
|
-
end
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
74
|
+
unless result
|
75
|
+
set_trace_func(tracer)
|
76
|
+
return nil
|
77
|
+
else
|
78
|
+
Thread.critical = old_critical
|
79
|
+
case block.arity
|
80
|
+
when 1 then yield(result)
|
81
|
+
else yield(result, extra_data)
|
82
|
+
end
|
79
83
|
end
|
80
84
|
end
|
81
|
-
end
|
85
|
+
end
|