maček
maček

Reputation: 77778

Rails: Create method available in all views and all models

I'd like to define a method that is available in both my views and my models

Say I have a view helper:

def foo(s)
  "hello #{s}"
end

A view might use the helper like this:

<div class="data"><%= foo(@user.name) %></div>

However, this <div> will be updated with a repeating ajax call. I'm using a to_json call in a controller returns data like so:

render :text => @item.to_json(:only => [...], :methods => [:foo])

This means, that I have to have foo defined in my Item model as well:

class Item
  def foo
    "hello #{name}"
  end
end

It'd be nice if I could have a DRY method that could be shared in both my views and my models.

Usage might look like this:

Helper

def say_hello(s)
  "hello #{s}"
end

User.rb model

def foo
  say_hello(name)
end

Item.rb model

def foo
  say_hello(label)
end

View

<div class="data"><%= item.foo %></div>

Controller

def observe
  @items = item.find(...)
  render :text => @items.to_json(:only=>[...], :methods=>[:foo])
end

I don't know the best way to handle this, but I don't want to completely go against best-practices here.

If you can think of a better way, I'm eager to learn!

Upvotes: 5

Views: 4627

Answers (2)

Hock
Hock

Reputation: 5804

You could add some modules, and then have in the models that you want the method to work (on a specific attribute) something like this:

The modules (add this to the lib folder)

module hasFoo
  def self.included(base)
    base.extend ClassMethods
  end
end

module ClassMethods
  def has_foo(method)
     define_method foo do
       field = self.send(method.to_sym)
       field
     end
  end
end

Then in your model, just add

has_foo :name

Then you can just call model.foo

And I think that would do it...

Upvotes: 5

Toby Hede
Toby Hede

Reputation: 37133

I would simply put this method in my model, which is available to both view and controller.

Model:

def say_hello
  "hello #{self.name}"
end

To add this to all of your models:

class AbstractModel < ActiveRecord::Base  
  self.abstract_class = true

   def say_hello
      self.respond_to?(:name) ? "hello #{self.name}" : "hello" 
   end

end

class MyModel < AbstractModel
end

The ternary operator on respond_to? handles the case where a model has no name column.

Upvotes: 1

Related Questions