123

I'm writing a model that handles user input from a text area. Following the advice from http://blog.caboo.se/articles/2008/8/25/sanitize-your-users-html-input, I'm cleaning up the input in the model before saving to database, using the before_validate callback.

The relevant parts of my model look like this:

include ActionView::Helpers::SanitizeHelper

class Post < ActiveRecord::Base {
  before_validation :clean_input

  ...

  protected

  def clean_input
    self.input = sanitize(self.input, :tags => %w(b i u))
  end
end

Needless to say, this doesn't work. I get the following error when I try and save a new Post.

undefined method `white_list_sanitizer' for #<Class:0xdeadbeef>

Apparently, SanitizeHelper creates an instance of HTML::WhiteListSanitizer, but when I mix it into my model it can't find HTML::WhiteListSanitizer. Why? What can I do about this to fix it?

awendt
  • 13,195
  • 5
  • 48
  • 66
O. Frabjous-Dey
  • 1,273
  • 2
  • 8
  • 5

8 Answers8

150

Just change the first line as follows :

include ActionView::Helpers

that will make it works.

UPDATE: For Rails 3 use:

ActionController::Base.helpers.sanitize(str)

Credit goes to lornc's answer

Community
  • 1
  • 1
Alfreddd
  • 1,941
  • 1
  • 13
  • 12
  • couldn't have said it better myself – Tilendor Jan 29 '09 at 00:09
  • 1
    Thanks. I got it to work by moving the include to inside of the class definition. – O. Frabjous-Dey Jan 29 '09 at 01:00
  • 1
    With this I get `stack level too deep`. It is in a before_save method. – Automatico Aug 01 '13 at 00:35
  • @Cort3z it means there is infinite loop, check on your field name and function name – ksugiarto Oct 09 '13 at 03:10
  • 46
    Please don't mix view layer concerns with active record models. That's a terrible practice. A much better approach is to put a standalone input data sanitizer object in front of AR and retrieve "clean" attributes from it. – solnic Jul 29 '14 at 10:52
  • I had to use `ActionController::Base.helpers.sanitize(str, tags: [])` – Sheharyar Jan 17 '17 at 00:11
  • 1
    This is a very bad solution and should be avoided like fire. Rails is based on MVC(Model View Controller) framework where helper comes in View part so you should not mix view helper methods with model. – jedi Jun 28 '18 at 22:44
  • Note that `ActionController::Base.helpers` only gets you the built in Rails helpers. If you also want your custom application-wide helpers, you need `ApplicationController.helpers`. – Henrik N Aug 30 '18 at 07:55
141

This gives you just the helper method without the side effects of loading every ActionView::Helpers method into your model:

ActionController::Base.helpers.sanitize(str)
lornc
  • 1,419
  • 1
  • 9
  • 3
  • 6
    For the slow people like me - you don't need to include anything, just use ActionController::Base.helpers.sanitize("On the string you want to sanitize") – Edward Apr 24 '12 at 10:45
  • 1
    Thank you, worked in Rails 2.3.14 while the accepted answer did not. – ChrisInEdmonton May 30 '12 at 17:30
  • I added a method to the application_helper, but I wasn't able to access it from the model using ActionController::Base.helpers.my_method(options) using Rails 3.0.3? – Tom Rossi Oct 15 '12 at 20:12
42

This works better for me:

Simple:

ApplicationController.helpers.my_helper_method

Advance:

class HelperProxy < ActionView::Base
  include ApplicationController.master_helper_module

  def current_user
    #let helpers act like we're a guest
    nil
  end       

  def self.instance
    @instance ||= new
  end
end

Source: http://makandracards.com/makandra/1307-how-to-use-helper-methods-inside-a-model

skozz
  • 2,662
  • 3
  • 26
  • 37
  • 1
    The `ApplicationController.master_helper_module` does not exist any more in Rails 3 and 4 it appears. The `ApplicationController.helpers` is a nice one though. – Samuel Mar 04 '15 at 13:43
  • I voted for this (the simple option) because it suited my needs - I only need one helper that is using information saved by a before filter in ApplicationController, so in my case making the association explicit is a reminder that there is a coupling. [Use case is multi domain app, that issues emails via a model notifier with a url link back to the app - this url changes depending on the domain of web request] – iheggie Mar 12 '15 at 07:45
26

To access helpers from your own controllers, just use:

OrdersController.helpers.order_number(@order)
Tarmo
  • 5,862
  • 2
  • 19
  • 12
  • 2
    Just using `ApplicationController.helpers.order_number(@order)`. That mean the `order_number` was locate on `Order Helper` – ksugiarto Oct 09 '13 at 03:15
  • 4
    @rowanu He's saying "to access (helpers from your own controllers)", not "(to access helpers) from your own controllers". – Ajedi32 Jan 12 '15 at 17:48
19

If you want to use a the my_helper_method inside a model, you can write:

ApplicationController.helpers.my_helper_method
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
Atchyut Nagabhairava
  • 1,295
  • 3
  • 16
  • 23
13

I wouldn't recommend any of these methods. Instead, put it within its own namespace.

class Post < ActiveRecord::Base
  def clean_input
    self.input = Helpers.sanitize(self.input, :tags => %w(b i u))
  end

  module Helpers
    extend ActionView::Helpers::SanitizeHelper
  end
end
axsuul
  • 7,370
  • 9
  • 54
  • 71
1

In Rails 7:

full_sanitizer = Rails::Html::FullSanitizer.new
full_sanitizer.sanitize("<a href="javascript:alert('hacked!')">Some dangerous input</a>")

List of available sanitizers: https://github.com/rails/rails-html-sanitizer#sanitizers

Your model would then be:

class Post < ActiveRecord::Base {
  before_validation :clean_input

  ...

  protected

  def clean_input
    full_sanitizer = Rails::Html::FullSanitizer.new
    self.input = full_sanitizer.sanitize(self.input, :tags => %w(b i u))
  end
end
TomDogg
  • 3,803
  • 5
  • 33
  • 60
1

Many suggests include or extend the entire helper library within the class, which is overkill.

I recommend just delegating the helper methods you need.

For example

class Post < ActiveRecord::Base
  delegate :sanitize, to: 'ActionController::Base.helpers'
end

This will give you access to sanitize within the model.

Viet
  • 3,257
  • 3
  • 28
  • 36