Rails ActiveRecord 中的私有新方法和创建方法?
Private new and create methods in a Rails ActiveRecord?
我有一个 ActiveRecord,它是 n 叉树的一个节点。模型中有很多class方法来创建根,追加叶子等等...
由于每次插入都会修改整棵树,我想避免外部 classes 通过 new 和 create 方法实例化我的模型。
知道怎么做吗?
(我在 Rails 4.0.2)
更新
所以我使用的树结构表示是一种非递归表示 - 按间隔。 The implementation is described there,但它是法语的。
基本上,每个节点都有一个 left_tree 和一个 right_tree 代表一个区间。子节点的left_tree和right_tree在父节点的区间内。这个表示让我在树上做的非常快select,但是在另一边有繁重的插入过程。
# Task schema
create_table "tasks", force: true do |t|
t.string "label"
t.integer "tree_level"
t.integer "left_tree"
t.integer "right_tree"
end
然后,为了插入,我需要所有树的间隔索引。
# Model Task
# Create the root of the tree. Only static method of the model
def self.create_root! label
Task.create! do |task|
task.tree_level = 1
task.left_tree = 1
task.right_tree = 2
task.label = label
end
end
# Method to add a child for a node. Task model
def create_child! label
new_task = Task.new
Task.transaction do
# Prepare the new task to be inserted in the intervals
new_task.left_tree = right_tree
new_task.right_tree = right_tree + 1
new_task.tree_level = tree_level + 1
new_task.label = label
# create an empty space in the tree
Task.where(
'right_tree >= :right_tree',
{ right_tree: right_tree }).update_all('right_tree = right_tree + 2')
Task.where(
'left_tree >= :right_tree',
{ right_tree: right_tree }).update_all('left_tree = left_tree + 2')
# Save the task, which have now a place in the tree.
new_task.save!
end
reload
return new_task
end
如您所见,模型永远不应在我的模型任务之外实例化。
我们应该创建一个根,然后通过 create_child!
方法从这个根创建整个树
您可以选择像这样更改界面:
# Just makin a sample base class.
# In your case it would be ActiveRecord::Base
class RecordBase
def self.create(label)
record = new
record.label = label
record.save
record
end
def save
true
end
end
class Task < RecordBase
# Make the given class methods private.
private_class_method :new, :create
# Creates and returns the root.
def self.create_root(label)
# Task.create won't work here since create is private.
create(label)
end
def self.create_child(parent, label)
# Task.new won't work here since new is private.
child = new
child.label = label
# other stuffs like :
# child.attr = parent.attr
child.save
child
end
def create_child(label)
self.class.create_child(self, label)
end
end
# Things that don't work :
# task = Task.new
# task = Task.create('label')
# Working part :
# Creates and gets the root.
root = Task.create_root('label')
# Creates a child
child = root.create_child('label')
用户可以调用 Task.create_child(parent, label)
,但我认为这不会有问题,因为它使用与 task.create_child(label)
.
相同的算法
我有一个 ActiveRecord,它是 n 叉树的一个节点。模型中有很多class方法来创建根,追加叶子等等...
由于每次插入都会修改整棵树,我想避免外部 classes 通过 new 和 create 方法实例化我的模型。
知道怎么做吗? (我在 Rails 4.0.2)
更新
所以我使用的树结构表示是一种非递归表示 - 按间隔。 The implementation is described there,但它是法语的。
基本上,每个节点都有一个 left_tree 和一个 right_tree 代表一个区间。子节点的left_tree和right_tree在父节点的区间内。这个表示让我在树上做的非常快select,但是在另一边有繁重的插入过程。
# Task schema
create_table "tasks", force: true do |t|
t.string "label"
t.integer "tree_level"
t.integer "left_tree"
t.integer "right_tree"
end
然后,为了插入,我需要所有树的间隔索引。
# Model Task
# Create the root of the tree. Only static method of the model
def self.create_root! label
Task.create! do |task|
task.tree_level = 1
task.left_tree = 1
task.right_tree = 2
task.label = label
end
end
# Method to add a child for a node. Task model
def create_child! label
new_task = Task.new
Task.transaction do
# Prepare the new task to be inserted in the intervals
new_task.left_tree = right_tree
new_task.right_tree = right_tree + 1
new_task.tree_level = tree_level + 1
new_task.label = label
# create an empty space in the tree
Task.where(
'right_tree >= :right_tree',
{ right_tree: right_tree }).update_all('right_tree = right_tree + 2')
Task.where(
'left_tree >= :right_tree',
{ right_tree: right_tree }).update_all('left_tree = left_tree + 2')
# Save the task, which have now a place in the tree.
new_task.save!
end
reload
return new_task
end
如您所见,模型永远不应在我的模型任务之外实例化。 我们应该创建一个根,然后通过 create_child!
方法从这个根创建整个树您可以选择像这样更改界面:
# Just makin a sample base class.
# In your case it would be ActiveRecord::Base
class RecordBase
def self.create(label)
record = new
record.label = label
record.save
record
end
def save
true
end
end
class Task < RecordBase
# Make the given class methods private.
private_class_method :new, :create
# Creates and returns the root.
def self.create_root(label)
# Task.create won't work here since create is private.
create(label)
end
def self.create_child(parent, label)
# Task.new won't work here since new is private.
child = new
child.label = label
# other stuffs like :
# child.attr = parent.attr
child.save
child
end
def create_child(label)
self.class.create_child(self, label)
end
end
# Things that don't work :
# task = Task.new
# task = Task.create('label')
# Working part :
# Creates and gets the root.
root = Task.create_root('label')
# Creates a child
child = root.create_child('label')
用户可以调用 Task.create_child(parent, label)
,但我认为这不会有问题,因为它使用与 task.create_child(label)
.