aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2004-12-15 00:46:26 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2004-12-15 00:46:26 +0000
commit95314be65be197b6c38c8c93e3f8d1e8b5b0b674 (patch)
tree832f47d883b445e1074a76eb49133623037af3de
parent0a8f382b80efcaf26e2f3a432d07c137b72dd0d3 (diff)
downloadrails-95314be65be197b6c38c8c93e3f8d1e8b5b0b674.tar.gz
rails-95314be65be197b6c38c8c93e3f8d1e8b5b0b674.tar.bz2
rails-95314be65be197b6c38c8c93e3f8d1e8b5b0b674.zip
Added tree mixin and unit tests for all the mixins
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@156 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rwxr-xr-xactiverecord/lib/active_record/fixtures.rb7
-rw-r--r--activerecord/lib/active_record/mixins/list.rb32
-rw-r--r--activerecord/lib/active_record/mixins/tree.rb36
-rwxr-xr-xactiverecord/test/fixtures/db_definitions/mysql.sql12
-rw-r--r--activerecord/test/fixtures/db_definitions/postgresql.sql12
-rw-r--r--activerecord/test/fixtures/db_definitions/sqlite.sql13
-rw-r--r--activerecord/test/fixtures/db_definitions/sqlserver.sql16
-rw-r--r--activerecord/test/fixtures/mixin.rb21
-rw-r--r--activerecord/test/fixtures/mixins.yml14
-rw-r--r--activerecord/test/fixtures/naked/csv/accounts.csv1
-rw-r--r--activerecord/test/fixtures/naked/yml/accounts.yml1
-rw-r--r--activerecord/test/fixtures/naked/yml/companies.yml1
-rw-r--r--activerecord/test/fixtures/naked/yml/courses.yml1
-rwxr-xr-xactiverecord/test/fixtures_test.rb19
-rw-r--r--activerecord/test/mixin_test.rb101
15 files changed, 271 insertions, 16 deletions
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 8447a33c7d..09d0f46390 100755
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -193,8 +193,11 @@ class Fixtures < Hash
def read_fixture_files
if File.file?(yaml_file_path)
# YAML fixtures
- YAML::load(erb_render(IO.read(yaml_file_path))).each do |name, data|
- self[name] = Fixture.new(data, @class_name)
+ begin
+ yaml = YAML::load(erb_render(IO.read(yaml_file_path)))
+ yaml.each { |name, data| self[name] = Fixture.new(data, @class_name) } if yaml
+ rescue Exception=>boom
+ raise Fixture::FormatError, "a YAML error occured parsing #{yaml_file_path}"
end
elsif File.file?(csv_file_path)
# CSV fixtures
diff --git a/activerecord/lib/active_record/mixins/list.rb b/activerecord/lib/active_record/mixins/list.rb
index 6910ac771f..bec4b33651 100644
--- a/activerecord/lib/active_record/mixins/list.rb
+++ b/activerecord/lib/active_record/mixins/list.rb
@@ -32,6 +32,12 @@ module ActiveRecord
base.before_destroy :remove_from_list
base.after_create :add_to_list_bottom
end
+
+ # can be overriden
+
+ def position_column
+ "position"
+ end
# Moving around on the list
@@ -86,22 +92,22 @@ module ActiveRecord
# Changing the position
def increment_position
- update_attribute "position", position.to_i + 1
+ update_attribute position_column, self.send(position_column).to_i + 1
end
def decrement_position
- update_attribute "position", position.to_i - 1
+ update_attribute position_column, self.send(position_column).to_i - 1
end
# Querying the position
def first?
- self.position == 1
+ self.send(position_column) == 1
end
def last?
- self.position == bottom_position_in_list
+ self.send(position_column) == bottom_position_in_list
end
private
@@ -110,51 +116,51 @@ module ActiveRecord
def higher_item
self.class.find_first(
- "#{scope_condition} AND position = #{(position.to_i - 1).to_s}"
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i - 1).to_s}"
)
end
def lower_item
self.class.find_first(
- "#{scope_condition} AND position = #{(position.to_i + 1).to_s}"
+ "#{scope_condition} AND #{position_column} = #{(send(position_column).to_i + 1).to_s}"
)
end
def bottom_position_in_list
item = bottom_item
- item ? item.position : 0
+ item ? item.send(position_column) : 0
end
def bottom_item
self.class.find_first(
"#{scope_condition} ",
- "position DESC"
+ "#{position_column} DESC"
)
end
def assume_bottom_position
- update_attribute "position", bottom_position_in_list.to_i + 1
+ update_attribute position_column, bottom_position_in_list.to_i + 1
end
def assume_top_position
- update_attribute "position", 1
+ update_attribute position_column, 1
end
def decrement_positions_on_lower_items
self.class.update_all(
- "position = (position - 1)", "#{scope_condition} AND position > #{position.to_i}"
+ "#{position_column} = (#{position_column} - 1)", "#{scope_condition} AND #{position_column} > #{send(position_column).to_i}"
)
end
def increment_positions_on_higher_items
self.class.update_all(
- "position = (position + 1)", "#{scope_condition} AND position < #{position.to_i}"
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition} AND #{position_column} < #{send(position_column)}"
)
end
def increment_positions_on_all_items
self.class.update_all(
- "position = (position + 1)", "#{scope_condition}"
+ "#{position_column} = (#{position_column} + 1)", "#{scope_condition}"
)
end
end
diff --git a/activerecord/lib/active_record/mixins/tree.rb b/activerecord/lib/active_record/mixins/tree.rb
new file mode 100644
index 0000000000..582aa09b4b
--- /dev/null
+++ b/activerecord/lib/active_record/mixins/tree.rb
@@ -0,0 +1,36 @@
+module ActiveRecord
+ module Mixins
+ # Including this mixin if you want to model a tree structure by providing a parent association and an children
+ # association. This mixin assumes that you have a column called parent_id
+ #
+ # class Category < ActiveRecord::Base
+ # include ActiveRecord::Mixins::Tree
+ # end
+ #
+ # Example :
+ # root
+ # \_ child1
+ # \_ sub-child1
+ #
+ # root = Category.create("name" => "root")
+ # child1 = root.children.create("name" => "child1")
+ # subchild1 = child1.children.create("name" => "subchild1")
+ #
+ # root.parent # => nil
+ # child1.parent # => root
+ # root.children # => [child1]
+ # root.children.first.children.first # => subchild1
+ module Tree
+
+ def self.append_features(base)
+ super
+
+ base.module_eval <<-associations
+ belongs_to :parent, :class_name => name, :foreign_key => "parent_id"
+ has_many :children, :class_name => name, :foreign_key => "parent_id", :order => "id", :dependent => true
+ associations
+
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/fixtures/db_definitions/mysql.sql b/activerecord/test/fixtures/db_definitions/mysql.sql
index 0b96c49dd3..6af00eb7f0 100755
--- a/activerecord/test/fixtures/db_definitions/mysql.sql
+++ b/activerecord/test/fixtures/db_definitions/mysql.sql
@@ -96,3 +96,15 @@ CREATE TABLE `colnametests` (
`references` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
+
+CREATE TABLE `mixins` (
+ `id` int(11) NOT NULL auto_increment,
+ `parent_id` int(11) default NULL,
+ `pos` int(11) default NULL,
+ `lft` int(11) default NULL,
+ `rgt` int(11) default NULL,
+ `root_id` int(11) default NULL,
+ `created_at` datetime default NULL,
+ `updated_at` datetime default NULL,
+ PRIMARY KEY (`id`)
+);
diff --git a/activerecord/test/fixtures/db_definitions/postgresql.sql b/activerecord/test/fixtures/db_definitions/postgresql.sql
index 87db0b7f3f..9bf1035ea3 100644
--- a/activerecord/test/fixtures/db_definitions/postgresql.sql
+++ b/activerecord/test/fixtures/db_definitions/postgresql.sql
@@ -113,3 +113,15 @@ CREATE TABLE colnametests (
id serial,
"references" integer NOT NULL
);
+
+CREATE TABLE mixins (
+ id serial,
+ parent_id integer,
+ pos integer,
+ lft integer,
+ rgt integer,
+ root_id integer,
+ created_at timestamp,
+ updated_at timestamp,
+ PRIMARY KEY (`id`)
+);
diff --git a/activerecord/test/fixtures/db_definitions/sqlite.sql b/activerecord/test/fixtures/db_definitions/sqlite.sql
index 91ca172789..9863fc779f 100644
--- a/activerecord/test/fixtures/db_definitions/sqlite.sql
+++ b/activerecord/test/fixtures/db_definitions/sqlite.sql
@@ -85,3 +85,16 @@ CREATE TABLE 'colnametests' (
'id' INTEGER NOT NULL PRIMARY KEY,
'references' INTEGER NOT NULL
);
+
+CREATE TABLE 'mixins' (
+ 'id' INTEGER NOT NULL PRIMARY KEY,
+ 'parent_id' INTEGER DEFAULT NULL,
+ 'pos' INTEGER DEFAULT NULL,
+ 'lft' INTEGER DEFAULT NULL,
+ 'rgt' INTEGER DEFAULT NULL,
+ 'root_id' INTEGER DEFAULT NULL,
+ 'created_at' DATETIME DEFAULT NULL,
+ 'updated_at' DATETIME DEFAULT NULL
+);
+
+
diff --git a/activerecord/test/fixtures/db_definitions/sqlserver.sql b/activerecord/test/fixtures/db_definitions/sqlserver.sql
index 0ae9780273..555ff00f90 100644
--- a/activerecord/test/fixtures/db_definitions/sqlserver.sql
+++ b/activerecord/test/fixtures/db_definitions/sqlserver.sql
@@ -93,4 +93,18 @@ CREATE TABLE colnametests (
id int NOT NULL IDENTITY(1, 1),
[references] int NOT NULL,
PRIMARY KEY (id)
-); \ No newline at end of file
+);
+
+CREATE TABLE mixins (
+ id int NOT NULL IDENTITY(1, 1),
+ parent_id int default NULL,
+ pos int default NULL,
+ lft int default NULL,
+ rgt int default NULL,
+ root_id int default NULL,
+ created_at datetime default NULL,
+ updated_at datetime default NULL,
+ PRIMARY KEY (id)
+);
+
+
diff --git a/activerecord/test/fixtures/mixin.rb b/activerecord/test/fixtures/mixin.rb
new file mode 100644
index 0000000000..589c1a8c23
--- /dev/null
+++ b/activerecord/test/fixtures/mixin.rb
@@ -0,0 +1,21 @@
+class Mixin < ActiveRecord::Base
+ include ActiveRecord::Mixins::Touch
+ include ActiveRecord::Mixins::Tree
+end
+
+class ListMixin < ActiveRecord::Base
+ include ActiveRecord::Mixins::List
+
+ def self.table_name
+ "mixins"
+ end
+
+ def scope_condition
+ "parent_id = #{self.parent_id}"
+ end
+
+ def position_column
+ "pos"
+ end
+
+end \ No newline at end of file
diff --git a/activerecord/test/fixtures/mixins.yml b/activerecord/test/fixtures/mixins.yml
new file mode 100644
index 0000000000..c74b1da51b
--- /dev/null
+++ b/activerecord/test/fixtures/mixins.yml
@@ -0,0 +1,14 @@
+first:
+ id: 1
+ pos: 1
+ parent_id: 0
+
+second:
+ id: 2
+ pos: 1
+ parent_id: 1
+
+third:
+ id: 3
+ pos: 2
+ parent_id: 1 \ No newline at end of file
diff --git a/activerecord/test/fixtures/naked/csv/accounts.csv b/activerecord/test/fixtures/naked/csv/accounts.csv
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/activerecord/test/fixtures/naked/csv/accounts.csv
@@ -0,0 +1 @@
+
diff --git a/activerecord/test/fixtures/naked/yml/accounts.yml b/activerecord/test/fixtures/naked/yml/accounts.yml
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/accounts.yml
@@ -0,0 +1 @@
+
diff --git a/activerecord/test/fixtures/naked/yml/companies.yml b/activerecord/test/fixtures/naked/yml/companies.yml
new file mode 100644
index 0000000000..2c151c203b
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/companies.yml
@@ -0,0 +1 @@
+# i wonder what will happen here
diff --git a/activerecord/test/fixtures/naked/yml/courses.yml b/activerecord/test/fixtures/naked/yml/courses.yml
new file mode 100644
index 0000000000..19f0805d8d
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/courses.yml
@@ -0,0 +1 @@
+qwerty
diff --git a/activerecord/test/fixtures_test.rb b/activerecord/test/fixtures_test.rb
index a971b9f6f8..4c450eb3cf 100755
--- a/activerecord/test/fixtures_test.rb
+++ b/activerecord/test/fixtures_test.rb
@@ -87,4 +87,23 @@ class FixturesTest < Test::Unit::TestCase
assert_equal 10, @developers.size
assert_equal "fixture_5", @dev_5.name
end
+
+ def test_empty_yaml_fixture
+ assert_not_nil Fixtures.new( Account.connection, "accounts", File.dirname(__FILE__) + "/fixtures/naked/yml/accounts")
+ end
+
+ def test_empty_yaml_fixture_with_a_comment_in_it
+ assert_not_nil Fixtures.new( Account.connection, "companies", File.dirname(__FILE__) + "/fixtures/naked/yml/companies")
+ end
+
+ def test_dirty_dirty_yaml_file
+ assert_raises(Fixture::FormatError) do
+ Fixtures.new( Account.connection, "courses", File.dirname(__FILE__) + "/fixtures/naked/yml/courses")
+ end
+ end
+
+ def test_empty_csv_fixtures
+ assert_not_nil Fixtures.new( Account.connection, "accounts", File.dirname(__FILE__) + "/fixtures/naked/csv/accounts")
+ end
+
end
diff --git a/activerecord/test/mixin_test.rb b/activerecord/test/mixin_test.rb
new file mode 100644
index 0000000000..cc56bc7cec
--- /dev/null
+++ b/activerecord/test/mixin_test.rb
@@ -0,0 +1,101 @@
+require 'abstract_unit'
+require 'active_record/mixins/tree'
+require 'active_record/mixins/list'
+require 'active_record/mixins/touch'
+require 'fixtures/mixin'
+
+class TreeTest < Test::Unit::TestCase
+ fixtures :mixins
+
+ def test_has_child
+ assert_equal true, @first.has_children?
+ assert_equal false, @second.has_children?
+ assert_equal false, @third.has_children?
+ end
+
+ def test_children
+ assert_equal @first.children, [@second, @third]
+ assert_equal @second.children, []
+ end
+
+ def test_parent
+ assert_equal @second.parent, @first
+ assert_equal @second.parent, @third.parent
+ assert_nil @first.parent
+ end
+
+ def test_insert
+ @extra = @first.children.create
+
+ assert @extra
+
+ assert_equal @extra.parent, @first
+ assert_equal [@second, @third, @extra], @first.children
+ end
+
+ def test_delete
+ assert_equal 3, Mixin.count
+ @first.destroy
+ assert_equal 0, Mixin.count
+ end
+end
+
+class TouchTest < Test::Unit::TestCase
+ fixtures :mixins
+
+ def test_update
+ assert_nil @first.updated_at
+ @first.save
+ assert_not_nil @first.updated_at
+ end
+
+ def test_create
+ @obj = Mixin.create({"parent" => @third})
+ assert_not_nil @obj.updated_at
+ assert_not_nil @obj.created_at
+ end
+
+
+end
+
+
+class ListTest < Test::Unit::TestCase
+ fixtures :mixins
+
+ def test_reordering
+
+ assert_equal [ListMixin.find(2), ListMixin.find(3)], ListMixin.find_all("parent_id=1", "pos")
+
+ ListMixin.find(2).move_lower
+
+ assert_equal [ListMixin.find(3), ListMixin.find(2)], ListMixin.find_all("parent_id=1", "pos")
+
+ ListMixin.find(2).move_higher
+
+ assert_equal [ListMixin.find(2), ListMixin.find(3)], ListMixin.find_all("parent_id=1", "pos")
+
+ end
+
+ def test_insert
+ new = ListMixin.create("parent_id"=>3)
+ assert_equal 1, new.pos
+ assert new.first?
+ assert new.last?
+
+ new = ListMixin.create("parent_id"=>3)
+ assert_equal 2, new.pos
+ assert !new.first?
+ assert new.last?
+
+ new = ListMixin.create("parent_id"=>3)
+ assert_equal 3, new.pos
+ assert !new.first?
+ assert new.last?
+
+ new = ListMixin.create("parent_id"=>2)
+ assert_equal 1, new.pos
+ assert new.first?
+ assert new.last?
+
+ end
+end \ No newline at end of file