Calling custom Ruby code based on something in your database
I am working on an application called Apphakker. In this application, users are able to complete assignments. When they complete an assignment they claim an achievement and they get awarded points.
These assignments are things like "create a controller in your Rails app" or "deploy an update to Heroku". Each assignment has it's own piece of validation code in the form of a Ruby class. For instance, I have a ControllerCreatedAssignment.
I need to be able to save wether a user has completed an assignment in the database. So, I created an Assignment model in my Rails apps that has a hasandbelongstomany relationship with Player (the user account). For every assignment available to players, I have an Assignment object in my database. Each assignment has a column that contains the associated Ruby class.
So, how do I go on and actually execute the appropriate Ruby class from a string column in the database? Here is the controller code responsible for checking if a Player has completed an assignment:
class AchievementsController < ApplicationController
before_filter :authenticate_player!
def claim
@assignment = Assignment.find(params[:id])
if @assignment.unlocked?
current_player.assignments << @assignment
render
else
render :nothing => true
end
end
end
The above controller fetches the Assignment object from the database based on the id that is entered. Then it calls unlocked? on the assignment to check if a player completed the assignment.
The code on the Assignment model looks like this:
class Assignment < ActiveRecord::Base
attr_accessible :name, :ruby_class
delegate :unlocked?, :to => :validator
def validator
@validator ||= ruby_class.constantize.new()
end
end
First of all, there is a delegate method, that delegates a call to the unlocked? method to an object that is returned by the validator method.
The validator method in its turn grabs the class name as a string from the database, constantizes it to a Ruby class, and calls the "new" method on it to initialize an object.
So, say I have an assignment in my database that needs to execute code from the ControllerCreatedAssignment class.
First, I grab the appropriate Assignment model object from the database by its primary key.
Then, I grab the string from the ruby_class* column, which in this case contains the string **"ControllerCreatedAssignment". By constantizing it, I get an actual Ruby class ControllerCreatedAssignment which has a unlocked? instance method. I call assignment.unlocked? which in its turn calls unlocked? on an object of the ControllerCreatedAssignment class, which returns true or false wether the assignment has been completed.
Here is how the ControllerCreatedAssignment might look like:
class ControllerCreatedAssignment
def unlocked?
# Clone git
# Check for a _controller in app/controllers
# return true or false
end
end