【问题标题】:Private new and create methods in a Rails ActiveRecord?Rails ActiveRecord 中的私有新方法和创建方法?
【发布时间】:2015-07-07 22:58:43
【问题描述】:

我有一个 ActiveRecord,它是 n 叉树的一个节点。模型中有很多类方法可以创建根、追加叶子等等……

由于每次插入都会修改整个树,我希望避免外部类通过 new 和 create 方法来实例化我的模型。

知道怎么做吗? (我在 Rails 4.0.2 中)

更新

所以我使用的树结构的表示是非递归表示 - 按间隔。 The implementation is described there,但它是法语的。

基本上,每个节点都有一个 left_tree 和一个 right_tree 代表一个区间。一个节点的子节点的left_tree和right_tree在父节点的区间内。这种表示允许我在树上进行非常快速的选择,但另一方面有繁重的插入过程。

# 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 方法创建整个树!

【问题讨论】:

  • 你能试着解释一下吗? 每次插入都会修改整棵树是什么意思?我们可以看一些代码吗?
  • 当然,我想避免发布我丑陋的代码(我通常不是开发 ruby​​)。我没有发布我所有的代码,但基本上这个想法就在那里。我的数据结构有某种 API,我想避免用户破坏一切。

标签: ruby-on-rails ruby activerecord constructor private


【解决方案1】:

您可以选择像这样更改界面:

# 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 不起作用,因为new 看起来不存在于课堂上方法self.create_childself.class.send :new 成功了,即使它很丑。至少现在数据结构用户更难做错事了。
  • 很高兴。是的,我的错,initialize 的覆盖是不必要的。我已经更新了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-07
  • 2013-05-06
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 2017-05-23
相关资源
最近更新 更多