How can I call an application controller method inside of a Ruby file? I can't use the traditional MVC architecture.
Thanks!
If the method is meant to be called from both the application controller and some other ruby file, then you need to move that method to another file, probably a Plain Old Ruby Object (PORO). Then, require that file from your controller and from whatever other file needs to use it.
It's a good idea to only have controller-related logic in controllers. Since you're calling this method from something besides a controller, it must not be strictly controller-related logic, so this is the perfect opportunity to move it.
If you have a method in the ApplicationController and you need to call it you can use a trick which is:
ApplicationController.new.#method_here
But you better move the method to a plugin and call it in the ApplicationController for a best practice.
More info here:
How do I call controller/view methods from the console in Rails?
https://stackoverflow.com/a/9159853/2552259
The best approach for your problem is to extract the method in question and put it in a module, like lib/my_utils.rb. Then you can require this file where ever you need it:
# lib/my_utils.rb
module MyUtils
def the_method_you_were_talking_about(args)
.. code ..
end
end
Then:
# application_controller.rb
require 'lib/my_utils.rb'
MyUtils.the_method_you_were_talking_about("and any data you need to pass to it")
And then you'd do the same thing from your other ruby file.
Related
I am trying to define some helper methods to be used in the app's controller, but it seems that rails don't even call the controller. just for the test I have the following controller in my app/controllers/my_engine/application_controller.rb and as the documents say rails should find it first and an error should raise because THIS_SHOULD_PRODUCE_ERROR is unknown, but the rspec happily executing without any errors!
class ApplicationController < ActionController::Base
THIS_SHOULD_PRODUCE_ERROR
end
I even tried to mimic the devise's way but the results are the same!
The guide section on the app directory suggests that the application_controller in an engine "will provide any common functionality for the controllers of the engine".
So I wouldn't expect that any additions to that controller will be available to all controllers in an application.
That also means that your application_controller is, I suspect, not getting called when you're running your test. Which would explain why you're not seeing an error.
In terms of how devise does it I think you need to be looking at how define_helpers works. The code you've linked to in your question is the application controller in the test app for the devise gem.
I noticed that I have got things wrong, and the application_controller in the engine does not get applied to application_controller in the app! Also, I couldn't figure out how the devise did it, but I have come up with the simple workaround for this:
require_relative 'controllers/helpers'
module Acu
module Injectors
class << self
ActiveSupport::Notifications.subscribe "start_processing.action_controller" do |**args|
eval((Acu::Configs.get :base_controller).to_s).class_eval do
include Acu::Controllers::Helpers
end
end
end
end
end
This will inject controller helpers to the user's base controller (which I get from the user, default: :ApplicationController) at the end of the class, which is perfect for me (but don't know how to add it to begging of the class if anyone needs it)
I was hired some days ago to update a Rails 4 app. In general the rspecs and the code look good however in the top of some controller a found this line:
delegate :edit_app_path, :new_app_payment_path, to: :view_context
Searching in the net, I found that the line is a way to load methods from the helpers inside a controller through a new instance of ActionView::Base class. I mean, is a way to do it instead of the classic:
include MyHelper
in the controller. My question is: is this really a good practice? is faster? AFAIK, view_context will load a new class with all the helpers and all the context of the view instead of one helper if I use the classic "include MyHelper". By the way :edit_app_path and :new_app_payment_path methods are in the same helper.
Should I remove the line?
Using view_context allows the controller to be blissfully ignorant of where the path is defined. If the helper file structure is refactored in future, the controller will continue humming along without requiring change.
Performance wise, I doubt the impact will be significant since all the code will have been loaded. Rails (and the Ruby standard library) creates new objects all the time.
I would like to override some of the methods defined in the file document_presenter.rb. How can I do this? This module is defined inside the Blacklight gem's "lib" directory.
Is there an easy way to do this? I'm fairly new to Ruby and Rails (coming from a pure Java background), so this is kind of difficult.
Thanks.
It sounds like you are talking about monkey patching the methods in the Backlight gem. You might want to read this post that explains more about monkey patching - and how not to break things badly!
In Ruby, you can always open an existing class, with the class keyword, and use the def keyword to redefine the original method.
class DocumentPresenter
def method_you_want_to_override
# Your code here.
end
end
So for example, you could put the above code into your lib folder:
lib/document_presenter.rb
See this answer re: auto-loading files in the lib folder.
After you've done that, whenever you call the method you've monkey patched on an instance of the DocumentPresenter class, the Ruby interpreter will run your code instead.
This is not recommended, as it can have dangerous and unpredictable results, as per then blog post I linked to.
A better practice, in Ruby 2, is to use Refinements.
I'm writing a configurable Rails engine. I have an authentication_helper configuration option to define which helper should be called in a before_action in all controllers needing authentication.
The problem is that I don't have access to the parent app's helpers from the engine's controllers. My understanding is that this happens because the engine is isolated.
I have considered using a block instead of a method name, but I'm not sure if that would work, or if I would be able to cleanly access the authorization logic from outside my controllers.
Active Admin, which I have used in the past, has a similar configuration option. I have noticed that their engine is not isolated, so perhaps I'm overrating the importance of engine isolation?
Is there an elegant way to have the benefits of engine isolation while also allowing this kind of customization? Or should I just forego isolation altogether?
EDIT #1
Brad Werth pointed my in the right direction, as this works with a regular controller inheriting from ApplicationController::Base:
module MyBigFancyEngine
class Engine < Rails::Engine
isolate_namespace MyBigFancyEngine
config.to_prepare do
# Make the implementing application's helpers available to the engine.
# This is required for the overriding of engine views and helpers to work correctly.
MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
However, my engine's ApplicationController inherits from RocketPant::Base, which does not provide a helper method. I've tried to use a simple include (which works fine for regular controllers), but that doesn't work either (the controller can't find the helper).
Any ideas?
You can expose the implementing application's helpers available to the engine by including the following code in your engine.rb file:
engine.rb
module MyBigFancyEngine
class Engine < Rails::Engine
isolate_namespace MyBigFancyEngine
config.to_prepare do
# Make the implementing application's helpers available to the engine.
# This is required for the overriding of engine views and helpers to work correctly.
MyBigFancyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
The RailsAdmin Engine is also isolated, but they have the same configuration options as you would like to implement. They have configurable before_filters for both authentication and authorization. Have a look at this.
As far as I can tell, they just subclass the parent controller like this::ApplicationController or instead you can configure one (ref).
For your controller you could just create your own EngineController, that inherits from RocketPant::Base and maybe just create a method there that calls the configured authentication method directly via send on the parent controller.
As the RocketPant::Base Class does not inherit from ApplicationController::Base I guess you have to find some custom way around this and can't go the normal ways for Rails Engines. Maybe you could also try to file an issue to the rocket_pant repo, to add the helper method. As far as I read they soon want to inherit from ApplicationController::Base anyway in 2.0 (ref).
I think you impulse on keeping the isolation is good - because a solution that relies on a method 'just being there' is a pain to debug if s.th. goes wrong in the app using your gem (i.e. typo in method name).
Further if your gem evolves, an some day it won't need the method, in an explicit way it's very convient to give a meainingful error:
You could provide a config method to be called in an initializer:
YourGem.configure do |config|
config.add_callback { MyApp.doIt() }
end
I found this discussion particularly insightful. There are also some interesting ideas in the Rails Engine API under Isolated engine helpers.
The Rails Engine API docs helped me figure out a good solution for url_helpers
You probably moved on by now, but if anyone else needs access to the "parent app" application helpers. You could always just include it explicitly in the application controller in your engine. like so:
include Rails.application.helpers
In the application I'm creating I use a gem. This gem has a Module with a method that is called by the gem when something changes. What I need to do is extend the functionality of this method. I cannot change the name of this method and I am not able to call a different method instead.
I'm talking about the rebuild method defined here:
https://github.com/the-teacher/the_sortable_tree/blob/master/app/controllers/the_sortable_tree_controller.rb
How can I add functionality to this method without touching the source?
You need to include the module with the function name which you want to override in the controller and then you can write the function with the same name and call super when you are done with your work.
class A
include TheSortableTreeController::Rebuild
def rebuild
# do something here
super
end
This way you will be able to perform yours as well as the operation of the gem as well. If you want to completely remove the dependency on the rebuild function remove super from the code.