Monkey patching a controller from a plugin


(Keith Newton) #1

Hi I’m new to Ruby and Rails so I apologize in advance if this has an obvious solution. I’m trying to overwrite some of the functions in various controllers - for example the destroy function in SessionController. When precompiling assets I get an error that ApplicationController does not exist. When stubbing out ApplicationController I can get the code to go through precompilation however my monkey patched version of the function isn’t called. I’m assuming this is because the actual SessionController is being loaded after the plugin? If so is there a way to work around this?


Monkey patching a model from a plugin
(Vikhyat Korrapati) #2

If the problem is that the controller is being loaded after the plugin, you can probably put your code in an after_initialize block. Example:

after_initialize do
  User.class_eval do
    def avatar_template
      uploaded_avatar_template
    end
  end
end

(Keith Newton) #3

@radq This still doesn’t work for me =(

Hey @sam for some reason this post doesn’t show up in my list of posts or topics or anywhere else in my profile (unless posting this reply fixes it for some reason). Also I can’t PM @radq.


(Jeff Atwood) #4

Yes it does, your post shows up just fine here


(Vikhyat Korrapati) #5

The new current user provider hook means that you shouldn’t need to monkey-patch SessionController now.


(Keith Newton) #6

@codinghorror The response I posted last night shows up under 301 Moved Permanently however the original post does not show up anywhere. In fact this topic wasn’t listed anywhere on my profile until I posted the response. Also I have been noticing a lot of things seem to be randomly breaking over the last few days. For example right now when I type @codinghorror the little intellisense box that usually pops up is not (though it was working last night). I was trying to send @radq a PM by clicking on his name from this post and clicking the ‘Private Message’ button but it did nothing. Last night search was broken, the little spinning graphic just kept going round and round with no results, and then randomly started working again ~10 minutes later.

@radq yes @sam added ways to override current user which is extremely useful and that takes care of my issues with SessionController however there are other controllers besides SessionController that I need to patch. For example UsersController exposes all of the API elements for creating accounts among other things and I need to monkey patch those so that they no longer do anything. I’m new to Ruby but it doesn’t seem like it should be this difficult just to patch a class so I don’t know if Discourse is doing something special with how it loads plugins or what. Maybe I’m just approaching it the wrong way?


(Jeff Atwood) #7

Did you try clearing your browser cache? I can’t repro any of that.


(Keith Newton) #8

@codinghorror Clearing my browser cache fixed the issue with the @ intellisense box not popping up however it did not fix the issue with PM’s. I am, however, getting errors in my JS console (didn’t notice this before, sry for not posting previously):

GET http://meta.discourse.org/topics/private-messages/radq.json 403 (Forbidden) application-1c1124695302c018c02af34ea5b2a1e6.js:3
Error while loading route: 
Object {readyState: 4, getResponseHeader: function, getAllResponseHeaders: function, setRequestHeader: function, overrideMimeType: function…}
 application-1c1124695302c018c02af34ea5b2a1e6.js:38
Uncaught #<Object> 

I’m using Chrome Version 30.0.1599.69 m. My repro is:

  1. Click radq on the left side under his avatar/picture
  2. Click Private Message in the little popup box

(Sam Saffron) #9

My intention if for monkey patching to be a complete final resort, working on slowly adding official hooks.

I am able to monkey patch models at ease here, I am fairly sure the same technique would work with controllers:


(Keith Newton) #10

Hi @sam I’ve tried monkey patching with following:

after_initialize do
  UsersController.class_eval do
    def create
      render json: { message: "hi" }
    end
  end
end 

and

after_initialize do
  class UsersController
    def create
      render json: { message: "hi" }
    end
  end
end

and

after_initialize do
  # NOTE: This fails to precompile with a NameError
  class UsersController < ApplicationController
    def create
      render json: { message: "hi" }
    end
  end
end

and none of them work. I’m testing using DevHTTP and when I do a post it just returns the same HTML page regardless of which of those are being used or if no attempt at patching is being made. Thanks for looking into this.


(Volkan Unsal) #11

You could also use Module#prepend:

http://ruby-doc.org/core-2.0/Module.html#method-i-prepend

class Foo
  def bar
    'Hello'
  end
end 

module FooExtensions
  def bar
    super + ' World'
  end
end

class Foo
  prepend FooExtensions # the only change to above: prepend instead of include
end

Foo.new.bar # => 'Hello World'

More on this method here.


(Avi Douglen) #12

Hi Sam, are these “official hooks” documented anywhere? Where can I find more about this?
Or should I stick to monkeypatching for now…


(Orlando Del Aguila) #13

Just in case someone is looking for an answer, this seems to be working correctly

  module MigratepasswordAfterPasswordReset
    def logon_after_password_reset
      @user.custom_fields.delete('migratepassword_policy')
      @user.save
      super
    end
  end

  ::UsersController.class_eval do
    prepend MigratepasswordAfterPasswordReset
  end