さて、あなたにはいくつかの選択肢があります。
1つ目は、あなたが言ったように、メソッド/関数名をレコードに保存し、そのレコードのクエストが完了したかどうかを評価する必要があるときはいつでもそのメソッドを呼び出すことです。大まかなコード:
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アルゴリズムなどを使用して評価できるようにプロダクションルールをモデル化すると、効率を上げることができます。