I have multiple controllers that I need to have call a method in the corresponding model to check a condition when the user attempts to create a new object. If the condition is met then I would like to redirect the user back to the index page. If the condition is not met then I would like to continue with the create.
For example, if I am in a model called "Project". I would like the controller to fire the method Project.some_condition before the create method. If the condition is true then do X else do Y.
Thank you in advance! Also, I know that I can put a before_create filter in each model but I'm trying to learn how to do this via metaprogramming.
I realize this won't help you work on your metaprogramming, but I believe you want to change Project.some_condition to Project.some_validation and then call #save in your controller.
I'm not clear how you would use metaprogramming to do what you've described. More fundamentally, I'd suggest that you avoid trying to use metaprogramming to solve problems that don't require metaprogramming; doing so will just make your code more difficult to maintain and understand. If you're interested in learning more about how metaprogramming works Dave Thomas has a nice set of screencasts on the topic that are easy to follow.
Thanks for the reply. A validation would work. However, what I'm trying to do is to avoid code duplication (which lead me down the metaprogramming path). Is there a way to create a custom validation that you could use against multiple models? For instance....
XXXXX being the model that I'm validating against
Again, don't be too quick to jump to metaprogramming for something like this. Tried and true object orientation will eliminate your code duplication far more easily and cleanly. You could put the validation on a base class and have each of the classes you care about derive from that class. Using inheritance in this way makes more sense if your classes have some form of substitutable relationship above and beyond the validation. Alternately, you could put your validation into a module and include it into each class. For example:
def self.included(klass) klass.instance_eval do validate :thing_is_within_limit end end private def thing_is_within_limit ... end
first, Adam is totally right that you should only turn to meta programming if it is truely necessary and plain OOP doesn't yield a solution. However, what you describe might be an "advice" as in AOP. This would mean that your "condition check" should be viewed as an "aspect" of your object model. In this case you could simulate a pointcut by using the Rails before filter in your controllers/application.rb.
class ApplicationController < ActionController::Base
In the "check_condition" method you could indeed use some rather ugly meta programming to call a validation method on your model (I didn't actually test this code, but something like this might work):
controller_name = self.class.to_s plural_name = controller_name.sub('Controller', '') model_name = plural_name.classify my_model = model.name.constantize my_model.check_condition(some_params)
Although I don't know whether this code works as is if you just copy/paste, you should be able to see the idea. I first get the name of the controller class (e.g. "ProjectsController"). Next I strip the "Controller" part to only get the pluralized name of the model (Rails convention, e.g. "Projects"). "classify" turns a plural name into a singular model name ("singularize" would probably also be possible here; e.g. "Project"). Finally "constantize" turns a string into a class on which you can call the static "check_condition" method (this method you would have to define for each model class).
I hope this helps. If anyone out there knows a more elegant solution (apart from Aquarium for Ruby ;)), please let us know!