2

如果我有类似任务的 Rails 模型(比如在游戏中),我会有名称、描述等属性。但我还需要一些与每个任务相关的代码,以确定玩家是否满足完成任务的要求。

将某些代码与模型中的记录相关联的最佳方法是什么?我应该在模型中创建一个 quest_function 属性并在创建新任务时为 models/quest.rb 文件中的每条记录编写一个函数吗?这看起来很笨拙且容易出错。

这样的事情应该如何在 Rails 中完成?

4

2 回答 2

4

好吧,你有几个选择。

第一个,如您所说,是在您的记录中存储一个方法/函数名称,然后在您需要评估该记录的该任务是否已完成时调用该方法。在粗略的代码中:

class Quest < ActiveRecord::Base
    attr_accessible :name, :quest_method

    def completed?
      self.send(quest_method)
    end

    def end_quest
       Monster.find_by_name('FINAL BOSS').dead?
    end
end

Quest.create name: 'End Quest', quest_method: 'end_quest'

这并不像你想象的那么糟糕。这本质上是单表继承的工作方式,但不是存储方法名,而是将类名存储在type列中。

class Quest < ActiveRecord::Base

    def finished?
        # default value, or throw an error
        false
    end

end

class EndQuest < Quest

    def finished?
        Monster.find_by_name('END BOSS').dead?
    end
end

EndQuest.create # have to do this for all quests

在任何一种情况下,您都将行为编写为代码,并且只是在记录中存储对所需行为的引用。您必须使用正确的类型或方法名称手动创建所有记录,并确保一切保持同步。但至少对于单表继承,ActiveRecord 会处理其中的一些问题。

然而,没有什么能阻止您将代码本身存储到数据库中。

class Quest < ActiveRecord::Base

    attr_accessible :name, :completed_expr

    def completed?
        eval(completed_expr)
    end

end

Quest.create name: 'END QUEST', 
  completed_expr: "Monster.find_by_name('BIG BOSS').dead?"

除非您确切知道自己在做什么,否则这当然是潜在的危险和容易出错的——但它确实为您提供了在运行时重写任务完成标准的完全灵活性。

最后,如果您想要最复杂和最复杂的,您可能想要考虑使用规则引擎(或者,使用 DSL 推出您自己的完整规则引擎)来包含您的业务(游戏世界)规则并将其存储。

class Quest < ActiveRecord::Base
    attr_accessible :name, :completion_rule

    def completed?
        RulesEngine.evaluate(completion_rule)
    end
end

Quest.create name: 'END QUEST', 
  completion_rule: "Monster(name: 'BIG BOSS', state: 'dead')"

主要优点是通过将特定于任务的行为或规则存储在 DSL(不是原始的 Ruby 代码)中,您可以获得一定程度的安全性——您不能只是意外地eval恶意或错误的代码导致系统崩溃。

如果您的生产规则以这样一种方式建模,您可以使用类似Rete 算法来评估它们,您也可以获得一些效率。

于 2013-01-24T08:13:04.390 回答
1

如果您想在创建任务时调用一个函数,那么您应该使用 rails 回调,例如after_createafter_update

http://guides.rubyonrails.org/active_record_validations_callbacks.html#callbacks-overview

于 2013-01-24T07:38:01.553 回答