Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Associate Rails Model Records With Code?

If I have a Rails model for something like Quests (like in a game) I'd have attributes like name, description, ect. but I'd also need some code associated with each quest to determine if the requirements have been met by a player to complete the quest.

What is the best way to associate some code with records from a model? Should I make a quest_function attribute in the model and write a function for each record in the models/quest.rb file when I create a new quest? This seems clunky and error prone.

How should something like this be done in Rails?

like image 436
DorkRawk Avatar asked Dec 10 '25 20:12

DorkRawk


1 Answers

Well, you have several options.

The first, like you said, is to store a method/function name in your record and then call that method whenever you need to evaluate whether that Quest for that record has been completed. In rough code:

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'

This isn't quite as bad as you think. This is essentially how single table inheritance works, but instead of storing a method name it stores the class name in the type column.

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

In either case, you're writing the behavior as code, and just storing a reference to the desired behavior in your record. You do have to create all the records by hand with the proper types or method names and make sure everything stays in sync. But at least with single-table inheritance ActiveRecord takes care of some of that.

There's nothing stopping you, however, from storing code itself into the database.

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?"

This, of course is potentially dangerous and error prone unless you know exactly what you're doing—but it does give you complete flexibility to rewrite the quest completion criteria at run-time.

Finally, if you want the most sophistication and complexity, you might want to look at using a rules engine (or, rolling out your own complete with DSL) to encompass your business (game world) rules and store that.

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')"

The main advantage is by storing your quest-specific behavior or rule in a DSL (which is not raw Ruby code) you get some level of security—you can't just accidentally eval malicious or errant code and bring the house down.

You can also gain some efficiency if your production rules are modeled in such a way where you can use something like the Rete algorithm to evaluate them.

like image 80
Alistair A. Israel Avatar answered Dec 12 '25 12:12

Alistair A. Israel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!