activerecord 1.14.4 → 1.15.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 +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
data/Rakefile
CHANGED
@@ -27,10 +27,14 @@ task :default => [ :test_mysql, :test_sqlite, :test_postgresql ]
|
|
27
27
|
|
28
28
|
# Run the unit tests
|
29
29
|
|
30
|
-
for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase )
|
30
|
+
for adapter in %w( mysql postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase frontbase )
|
31
31
|
Rake::TestTask.new("test_#{adapter}") { |t|
|
32
32
|
t.libs << "test" << "test/connections/native_#{adapter}"
|
33
|
-
|
33
|
+
if adapter =~ /^sqlserver/
|
34
|
+
t.pattern = "test/**/*_test{,_sqlserver}.rb"
|
35
|
+
else
|
36
|
+
t.pattern = "test/**/*_test{,_#{adapter}}.rb"
|
37
|
+
end
|
34
38
|
t.verbose = true
|
35
39
|
}
|
36
40
|
end
|
@@ -41,6 +45,8 @@ desc 'Build the MySQL test databases'
|
|
41
45
|
task :build_mysql_databases do
|
42
46
|
%x( mysqladmin create activerecord_unittest )
|
43
47
|
%x( mysqladmin create activerecord_unittest2 )
|
48
|
+
%x( mysql -e "grant all on activerecord_unittest.* to rails@localhost" )
|
49
|
+
%x( mysql -e "grant all on activerecord_unittest2.* to rails@localhost" )
|
44
50
|
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
|
45
51
|
%x( mysql activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql2.sql')} )
|
46
52
|
end
|
@@ -56,21 +62,60 @@ task :rebuild_mysql_databases => [:drop_mysql_databases, :build_mysql_databases]
|
|
56
62
|
|
57
63
|
desc 'Build the PostgreSQL test databases'
|
58
64
|
task :build_postgresql_databases do
|
59
|
-
%x( createdb activerecord_unittest )
|
60
|
-
%x( createdb activerecord_unittest2 )
|
61
|
-
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} )
|
62
|
-
%x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
|
65
|
+
%x( createdb -U postgres activerecord_unittest )
|
66
|
+
%x( createdb -U postgres activerecord_unittest2 )
|
67
|
+
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} postgres )
|
68
|
+
%x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} postgres )
|
63
69
|
end
|
64
70
|
|
65
71
|
desc 'Drop the PostgreSQL test databases'
|
66
72
|
task :drop_postgresql_databases do
|
67
|
-
%x( dropdb
|
68
|
-
%x( dropdb
|
73
|
+
%x( dropdb -U postgres activerecord_unittest )
|
74
|
+
%x( dropdb -U postgres activerecord_unittest2 )
|
69
75
|
end
|
70
76
|
|
71
77
|
desc 'Rebuild the PostgreSQL test databases'
|
72
78
|
task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgresql_databases]
|
73
79
|
|
80
|
+
desc 'Build the FrontBase test databases'
|
81
|
+
task :build_frontbase_databases => :rebuild_frontbase_databases
|
82
|
+
|
83
|
+
desc 'Rebuild the FrontBase test databases'
|
84
|
+
task :rebuild_frontbase_databases do
|
85
|
+
build_frontbase_database = Proc.new do |db_name, sql_definition_file|
|
86
|
+
%(
|
87
|
+
STOP DATABASE #{db_name};
|
88
|
+
DELETE DATABASE #{db_name};
|
89
|
+
CREATE DATABASE #{db_name};
|
90
|
+
|
91
|
+
CONNECT TO #{db_name} AS SESSION_NAME USER _SYSTEM;
|
92
|
+
SET COMMIT FALSE;
|
93
|
+
|
94
|
+
CREATE USER RAILS;
|
95
|
+
CREATE SCHEMA RAILS AUTHORIZATION RAILS;
|
96
|
+
COMMIT;
|
97
|
+
|
98
|
+
SET SESSION AUTHORIZATION RAILS;
|
99
|
+
SCRIPT '#{sql_definition_file}';
|
100
|
+
|
101
|
+
COMMIT;
|
102
|
+
|
103
|
+
DISCONNECT ALL;
|
104
|
+
)
|
105
|
+
end
|
106
|
+
create_activerecord_unittest = build_frontbase_database['activerecord_unittest', File.join(SCHEMA_PATH, 'frontbase.sql')]
|
107
|
+
create_activerecord_unittest2 = build_frontbase_database['activerecord_unittest2', File.join(SCHEMA_PATH, 'frontbase2.sql')]
|
108
|
+
execute_frontbase_sql = Proc.new do |sql|
|
109
|
+
system(<<-SHELL)
|
110
|
+
/Library/FrontBase/bin/sql92 <<-SQL
|
111
|
+
#{sql}
|
112
|
+
SQL
|
113
|
+
SHELL
|
114
|
+
end
|
115
|
+
execute_frontbase_sql[create_activerecord_unittest]
|
116
|
+
execute_frontbase_sql[create_activerecord_unittest2]
|
117
|
+
end
|
118
|
+
|
74
119
|
# Generate the RDoc documentation
|
75
120
|
|
76
121
|
Rake::RDocTask.new { |rdoc|
|
@@ -106,7 +151,7 @@ spec = Gem::Specification.new do |s|
|
|
106
151
|
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
107
152
|
end
|
108
153
|
|
109
|
-
s.add_dependency('activesupport', '= 1.
|
154
|
+
s.add_dependency('activesupport', '= 1.4.0' + PKG_BUILD)
|
110
155
|
|
111
156
|
s.files.delete "test/fixtures/fixture_database.sqlite"
|
112
157
|
s.files.delete "test/fixtures/fixture_database_2.sqlite"
|
@@ -178,4 +223,4 @@ task :release => [ :package ] do
|
|
178
223
|
puts release_command
|
179
224
|
system(release_command)
|
180
225
|
end
|
181
|
-
end
|
226
|
+
end
|
data/lib/active_record.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#--
|
2
|
-
# Copyright (c) 2004 David Heinemeier Hansson
|
2
|
+
# Copyright (c) 2004-2006 David Heinemeier Hansson
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -46,14 +46,18 @@ require 'active_record/timestamp'
|
|
46
46
|
require 'active_record/acts/list'
|
47
47
|
require 'active_record/acts/tree'
|
48
48
|
require 'active_record/acts/nested_set'
|
49
|
-
require 'active_record/locking'
|
49
|
+
require 'active_record/locking/optimistic'
|
50
|
+
require 'active_record/locking/pessimistic'
|
50
51
|
require 'active_record/migration'
|
51
52
|
require 'active_record/schema'
|
52
53
|
require 'active_record/calculations'
|
54
|
+
require 'active_record/xml_serialization'
|
55
|
+
require 'active_record/attribute_methods'
|
53
56
|
|
54
57
|
ActiveRecord::Base.class_eval do
|
55
58
|
include ActiveRecord::Validations
|
56
|
-
include ActiveRecord::Locking
|
59
|
+
include ActiveRecord::Locking::Optimistic
|
60
|
+
include ActiveRecord::Locking::Pessimistic
|
57
61
|
include ActiveRecord::Callbacks
|
58
62
|
include ActiveRecord::Observing
|
59
63
|
include ActiveRecord::Timestamp
|
@@ -65,10 +69,12 @@ ActiveRecord::Base.class_eval do
|
|
65
69
|
include ActiveRecord::Acts::List
|
66
70
|
include ActiveRecord::Acts::NestedSet
|
67
71
|
include ActiveRecord::Calculations
|
72
|
+
include ActiveRecord::XmlSerialization
|
73
|
+
include ActiveRecord::AttributeMethods
|
68
74
|
end
|
69
75
|
|
70
76
|
unless defined?(RAILS_CONNECTION_ADAPTERS)
|
71
|
-
RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase )
|
77
|
+
RAILS_CONNECTION_ADAPTERS = %w( mysql postgresql sqlite firebird sqlserver db2 oracle sybase openbase frontbase )
|
72
78
|
end
|
73
79
|
|
74
80
|
RAILS_CONNECTION_ADAPTERS.each do |adapter|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Acts #:nodoc:
|
3
3
|
module List #:nodoc:
|
4
|
-
def self.
|
5
|
-
super
|
4
|
+
def self.included(base)
|
6
5
|
base.extend(ClassMethods)
|
7
6
|
end
|
8
7
|
|
@@ -78,7 +77,8 @@ module ActiveRecord
|
|
78
77
|
def insert_at(position = 1)
|
79
78
|
insert_at_position(position)
|
80
79
|
end
|
81
|
-
|
80
|
+
|
81
|
+
# Swap positions with the next lower item, if one exists.
|
82
82
|
def move_lower
|
83
83
|
return unless lower_item
|
84
84
|
|
@@ -88,6 +88,7 @@ module ActiveRecord
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
+
# Swap positions with the next higher item, if one exists.
|
91
92
|
def move_higher
|
92
93
|
return unless higher_item
|
93
94
|
|
@@ -97,6 +98,8 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
101
|
+
# Move to the bottom of the list. If the item is already in the list, the items below it have their
|
102
|
+
# position adjusted accordingly.
|
100
103
|
def move_to_bottom
|
101
104
|
return unless in_list?
|
102
105
|
acts_as_list_class.transaction do
|
@@ -105,6 +108,8 @@ module ActiveRecord
|
|
105
108
|
end
|
106
109
|
end
|
107
110
|
|
111
|
+
# Move to the top of the list. If the item is already in the list, the items above it have their
|
112
|
+
# position adjusted accordingly.
|
108
113
|
def move_to_top
|
109
114
|
return unless in_list?
|
110
115
|
acts_as_list_class.transaction do
|
@@ -112,31 +117,36 @@ module ActiveRecord
|
|
112
117
|
assume_top_position
|
113
118
|
end
|
114
119
|
end
|
115
|
-
|
120
|
+
|
116
121
|
def remove_from_list
|
117
122
|
decrement_positions_on_lower_items if in_list?
|
118
123
|
end
|
119
124
|
|
125
|
+
# Increase the position of this item without adjusting the rest of the list.
|
120
126
|
def increment_position
|
121
127
|
return unless in_list?
|
122
128
|
update_attribute position_column, self.send(position_column).to_i + 1
|
123
129
|
end
|
124
130
|
|
131
|
+
# Decrease the position of this item without adjusting the rest of the list.
|
125
132
|
def decrement_position
|
126
133
|
return unless in_list?
|
127
134
|
update_attribute position_column, self.send(position_column).to_i - 1
|
128
135
|
end
|
129
136
|
|
137
|
+
# Return true if this object is the first in the list.
|
130
138
|
def first?
|
131
139
|
return false unless in_list?
|
132
140
|
self.send(position_column) == 1
|
133
141
|
end
|
134
142
|
|
143
|
+
# Return true if this object is the last in the list.
|
135
144
|
def last?
|
136
145
|
return false unless in_list?
|
137
146
|
self.send(position_column) == bottom_position_in_list
|
138
147
|
end
|
139
148
|
|
149
|
+
# Return the next higher item in the list.
|
140
150
|
def higher_item
|
141
151
|
return nil unless in_list?
|
142
152
|
acts_as_list_class.find(:first, :conditions =>
|
@@ -144,6 +154,7 @@ module ActiveRecord
|
|
144
154
|
)
|
145
155
|
end
|
146
156
|
|
157
|
+
# Return the next lower item in the list.
|
147
158
|
def lower_item
|
148
159
|
return nil unless in_list?
|
149
160
|
acts_as_list_class.find(:first, :conditions =>
|
@@ -1,8 +1,7 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Acts #:nodoc:
|
3
3
|
module NestedSet #:nodoc:
|
4
|
-
def self.
|
5
|
-
super
|
4
|
+
def self.included(base)
|
6
5
|
base.extend(ClassMethods)
|
7
6
|
end
|
8
7
|
|
@@ -164,9 +163,9 @@ module ActiveRecord
|
|
164
163
|
child[left_col_name] = right_bound
|
165
164
|
child[right_col_name] = right_bound + 1
|
166
165
|
self[right_col_name] += 2
|
167
|
-
self.class.transaction {
|
168
|
-
self.class.update_all( "#{left_col_name} = (#{left_col_name} + 2)", "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
|
169
|
-
self.class.update_all( "#{right_col_name} = (#{right_col_name} + 2)", "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
|
166
|
+
self.class.base_class.transaction {
|
167
|
+
self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} + 2)", "#{scope_condition} AND #{left_col_name} >= #{right_bound}" )
|
168
|
+
self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} + 2)", "#{scope_condition} AND #{right_col_name} >= #{right_bound}" )
|
170
169
|
self.save
|
171
170
|
child.save
|
172
171
|
}
|
@@ -181,17 +180,17 @@ module ActiveRecord
|
|
181
180
|
|
182
181
|
# Returns a set of itself and all of its nested children
|
183
182
|
def full_set
|
184
|
-
self.class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} and #{self[right_col_name]})" )
|
183
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} BETWEEN #{self[left_col_name]} and #{self[right_col_name]})" )
|
185
184
|
end
|
186
185
|
|
187
186
|
# Returns a set of all of its children and nested children
|
188
187
|
def all_children
|
189
|
-
self.class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} > #{self[left_col_name]}) and (#{right_col_name} < #{self[right_col_name]})" )
|
188
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} AND (#{left_col_name} > #{self[left_col_name]}) and (#{right_col_name} < #{self[right_col_name]})" )
|
190
189
|
end
|
191
190
|
|
192
191
|
# Returns a set of only this entry's immediate children
|
193
192
|
def direct_children
|
194
|
-
self.class.find(:all, :conditions => "#{scope_condition} and #{parent_column} = #{self.id}")
|
193
|
+
self.class.base_class.find(:all, :conditions => "#{scope_condition} and #{parent_column} = #{self.id}")
|
195
194
|
end
|
196
195
|
|
197
196
|
# Prunes a branch off of the tree, shifting all of the elements on the right
|
@@ -200,10 +199,10 @@ module ActiveRecord
|
|
200
199
|
return if self[right_col_name].nil? || self[left_col_name].nil?
|
201
200
|
dif = self[right_col_name] - self[left_col_name] + 1
|
202
201
|
|
203
|
-
self.class.transaction {
|
204
|
-
self.class.delete_all( "#{scope_condition} and #{left_col_name} > #{self[left_col_name]} and #{right_col_name} < #{self[right_col_name]}" )
|
205
|
-
self.class.update_all( "#{left_col_name} = (#{left_col_name} - #{dif})", "#{scope_condition} AND #{left_col_name} >= #{self[right_col_name]}" )
|
206
|
-
self.class.update_all( "#{right_col_name} = (#{right_col_name} - #{dif} )", "#{scope_condition} AND #{right_col_name} >= #{self[right_col_name]}" )
|
202
|
+
self.class.base_class.transaction {
|
203
|
+
self.class.base_class.delete_all( "#{scope_condition} and #{left_col_name} > #{self[left_col_name]} and #{right_col_name} < #{self[right_col_name]}" )
|
204
|
+
self.class.base_class.update_all( "#{left_col_name} = (#{left_col_name} - #{dif})", "#{scope_condition} AND #{left_col_name} >= #{self[right_col_name]}" )
|
205
|
+
self.class.base_class.update_all( "#{right_col_name} = (#{right_col_name} - #{dif} )", "#{scope_condition} AND #{right_col_name} >= #{self[right_col_name]}" )
|
207
206
|
}
|
208
207
|
end
|
209
208
|
end
|
@@ -1,21 +1,20 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Acts #:nodoc:
|
3
3
|
module Tree #:nodoc:
|
4
|
-
def self.
|
5
|
-
|
6
|
-
|
7
|
-
end
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
8
7
|
|
9
|
-
# Specify this act if you want to model a tree structure by providing a parent association and a children
|
8
|
+
# Specify this act if you want to model a tree structure by providing a parent association and a children
|
10
9
|
# association. This act requires that you have a foreign key column, which by default is called parent_id.
|
11
|
-
#
|
10
|
+
#
|
12
11
|
# class Category < ActiveRecord::Base
|
13
12
|
# acts_as_tree :order => "name"
|
14
13
|
# end
|
15
|
-
#
|
16
|
-
# Example
|
14
|
+
#
|
15
|
+
# Example:
|
17
16
|
# root
|
18
|
-
# \_ child1
|
17
|
+
# \_ child1
|
19
18
|
# \_ subchild1
|
20
19
|
# \_ subchild2
|
21
20
|
#
|
@@ -28,7 +27,7 @@ module ActiveRecord
|
|
28
27
|
# root.children # => [child1]
|
29
28
|
# root.children.first.children.first # => subchild1
|
30
29
|
#
|
31
|
-
# In addition to the parent and children associations, the following instance methods are added to the class
|
30
|
+
# In addition to the parent and children associations, the following instance methods are added to the class
|
32
31
|
# after specifying the act:
|
33
32
|
# * siblings : Returns all the children of the parent, excluding the current node ([ subchild2 ] when called from subchild1)
|
34
33
|
# * self_and_siblings : Returns all the children of the parent, including the current node ([ subchild1, subchild2 ] when called from subchild1)
|
@@ -49,7 +48,7 @@ module ActiveRecord
|
|
49
48
|
|
50
49
|
class_eval <<-EOV
|
51
50
|
include ActiveRecord::Acts::Tree::InstanceMethods
|
52
|
-
|
51
|
+
|
53
52
|
def self.roots
|
54
53
|
find(:all, :conditions => "#{configuration[:foreign_key]} IS NULL", :order => #{configuration[:order].nil? ? "nil" : %Q{"#{configuration[:order]}"}})
|
55
54
|
end
|
@@ -67,13 +66,13 @@ module ActiveRecord
|
|
67
66
|
# subchild1.ancestors # => [child1, root]
|
68
67
|
def ancestors
|
69
68
|
node, nodes = self, []
|
70
|
-
nodes << node = node.parent
|
69
|
+
nodes << node = node.parent while node.parent
|
71
70
|
nodes
|
72
71
|
end
|
73
72
|
|
74
73
|
def root
|
75
74
|
node = self
|
76
|
-
node = node.parent
|
75
|
+
node = node.parent while node.parent
|
77
76
|
node
|
78
77
|
end
|
79
78
|
|
@@ -82,7 +81,7 @@ module ActiveRecord
|
|
82
81
|
end
|
83
82
|
|
84
83
|
def self_and_siblings
|
85
|
-
|
84
|
+
parent ? parent.children : self.class.roots
|
86
85
|
end
|
87
86
|
end
|
88
87
|
end
|
@@ -109,8 +109,8 @@ module ActiveRecord
|
|
109
109
|
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not keeping value objects
|
110
110
|
# immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
|
111
111
|
module ClassMethods
|
112
|
-
# Adds
|
113
|
-
# <tt>composed_of :address</tt>
|
112
|
+
# Adds reader and writer methods for manipulating a value object:
|
113
|
+
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
|
114
114
|
#
|
115
115
|
# Options are:
|
116
116
|
# * <tt>:class_name</tt> - specify the class name of the association. Use it only if that name can't be inferred
|
@@ -118,49 +118,73 @@ module ActiveRecord
|
|
118
118
|
# if the real class name is +CompanyAddress+, you'll have to specify it with this option.
|
119
119
|
# * <tt>:mapping</tt> - specifies a number of mapping arrays (attribute, parameter) that bind an attribute name
|
120
120
|
# to a constructor parameter on the value class.
|
121
|
+
# * <tt>:allow_nil</tt> - specifies that the aggregate object will not be instantiated when all mapped
|
122
|
+
# attributes are nil. Setting the aggregate class to nil has the effect of writing nil to all mapped attributes.
|
123
|
+
# This defaults to false.
|
121
124
|
#
|
122
125
|
# Option examples:
|
123
126
|
# composed_of :temperature, :mapping => %w(reading celsius)
|
124
127
|
# composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
|
125
128
|
# composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
|
126
129
|
# composed_of :gps_location
|
130
|
+
# composed_of :gps_location, :allow_nil => true
|
131
|
+
#
|
127
132
|
def composed_of(part_id, options = {})
|
128
|
-
options.assert_valid_keys(:class_name, :mapping)
|
133
|
+
options.assert_valid_keys(:class_name, :mapping, :allow_nil)
|
129
134
|
|
130
135
|
name = part_id.id2name
|
131
|
-
class_name = options[:class_name] ||
|
132
|
-
mapping = options[:mapping]
|
136
|
+
class_name = options[:class_name] || name.camelize
|
137
|
+
mapping = options[:mapping] || [ name, name ]
|
138
|
+
allow_nil = options[:allow_nil] || false
|
133
139
|
|
134
|
-
reader_method(name, class_name, mapping)
|
135
|
-
writer_method(name, class_name, mapping)
|
140
|
+
reader_method(name, class_name, mapping, allow_nil)
|
141
|
+
writer_method(name, class_name, mapping, allow_nil)
|
136
142
|
|
137
143
|
create_reflection(:composed_of, part_id, options, self)
|
138
144
|
end
|
139
145
|
|
140
146
|
private
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
147
|
+
def reader_method(name, class_name, mapping, allow_nil)
|
148
|
+
mapping = (Array === mapping.first ? mapping : [ mapping ])
|
149
|
+
|
150
|
+
allow_nil_condition = if allow_nil
|
151
|
+
mapping.collect { |pair| "!read_attribute(\"#{pair.first}\").nil?"}.join(" && ")
|
152
|
+
else
|
153
|
+
"true"
|
154
|
+
end
|
155
|
+
|
146
156
|
module_eval <<-end_eval
|
147
157
|
def #{name}(force_reload = false)
|
148
|
-
if @#{name}.nil? || force_reload
|
149
|
-
@#{name} = #{class_name}.new(#{
|
158
|
+
if (@#{name}.nil? || force_reload) && #{allow_nil_condition}
|
159
|
+
@#{name} = #{class_name}.new(#{mapping.collect { |pair| "read_attribute(\"#{pair.first}\")"}.join(", ")})
|
150
160
|
end
|
151
|
-
|
152
161
|
return @#{name}
|
153
162
|
end
|
154
163
|
end_eval
|
155
164
|
end
|
156
165
|
|
157
|
-
def writer_method(name, class_name, mapping)
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
166
|
+
def writer_method(name, class_name, mapping, allow_nil)
|
167
|
+
mapping = (Array === mapping.first ? mapping : [ mapping ])
|
168
|
+
|
169
|
+
if allow_nil
|
170
|
+
module_eval <<-end_eval
|
171
|
+
def #{name}=(part)
|
172
|
+
if part.nil?
|
173
|
+
#{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = nil" }.join("\n")}
|
174
|
+
else
|
175
|
+
@#{name} = part.freeze
|
176
|
+
#{mapping.collect { |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end_eval
|
180
|
+
else
|
181
|
+
module_eval <<-end_eval
|
182
|
+
def #{name}=(part)
|
183
|
+
@#{name} = part.freeze
|
184
|
+
#{mapping.collect{ |pair| "@attributes[\"#{pair.first}\"] = part.#{pair.last}" }.join("\n")}
|
185
|
+
end
|
186
|
+
end_eval
|
187
|
+
end
|
164
188
|
end
|
165
189
|
end
|
166
190
|
end
|