We are having a problem with the Rails ActionController::Base.view_paths. We are running a multi-tenant application and allow different themes for each tenant. now we have the problem that the theme view_paths dont reset on every request but stack up whenever we use prepend_view_path in the controller.
#paths=
[
...
#path="/app/themes/theme2/views",
...
#path="/app/themes/theme1/views",
]
we are using
ActionController::Base.prepend_view_path "app/themes/#{Theme.current}/views/"
in the controller.
Do you have an idea how we can force rails to generate view_paths new on every request?
We found the problem.
We used ApplicationController.renderer in a service class (outside of the controller). Manipulating the ApplicationController.view_paths outside the ApplicationController forced Rails to carry the view_path over to the next request.
Related
My app uses global variables like logos, app name, etc retrieved from the database and shown on different controllers and views. I put it in ApplicationController to be available to all, but I find that the individual controllers repeat the same query sometimes.
class ApplicationController < ActionController::Base
$image = Setting.find_by_name('image').value
$city = Setting.find_by_name('city').value
$currency = Setting.find_by_name('currency').value
end
Is there a way to make the same variables available to all controllers (and users) with just a one-time query with the variables saved on memory, such as when the app starts up?
You could attempt to use initializers.
Rail will load all files in config/initializers/ folder when the server starts up. This can work as a place to initialize application-wide variables. We could create a file inventory.rb file in the initializers directory:
at config/initializers/inventory.rb
module Inventory
class Count
Orders = Order.all
end
end
Inventory::Count::Orders
# => "[your orders will show here]"
These will only be loaded once when the server is started or restarted. As such this works well if the values you need won't change. If they will change I don't think there is a good way to avoid running multiple queries.
Whats about caching? Rails is using SQL Caching and you can use Low Level Caching. See the guides (1.7 and 1.8): https://guides.rubyonrails.org/caching_with_rails.html#low-level-caching
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'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
I have a class variable that I initialize, that later magically becomes uninitialized by itself... When developing locally on rails' default server (WEBrick), there is no problem. This only happens on nginx on ec2. Here's some code..
def TestController < ApplicationController
##classVariable = ""
def index
##classVariable = "What's up homie"
log(##classVariable)
end
def callThisMethodViaAJAXFromJavascript
log("reached this method")
log(##classVariable)
end
def log(str)
File.write("aValidPath", str)
end
end
Here's what I do: When I load the page on test/index, the index method executes, and properly logs ##classVariable as:
"What's up homie"
But when I call the method callThisMethodViaAJAXFromJavascript via AJAX from the frontend, my log file looks like:
"reached this method"
""
Again, this ONLY occurs on nginx on, ec2 (OS is ubuntu). When I run locally on WEBrick, this NEVER happens.
Any ideas? Thank you very much.
You don't say how you're using nginx (as a reverse proxy to some unicorn instances, with passenger etc) but either way you would typically have multiple instances of your application. Each one is a separate process so setting a class variable in one process has no effect on the other process.
Nginx will balance requests between the rails instances - so the index page is served by one instance and the ajax action will frequently be served by another process where the clas variable is still the empty string.
In development with webrick there is only one rails instance so you don't encounter this problem. I'm not sure what you were trying to do but class variables are not a good way to preserve state across requests
Per #Frederick's answer, I used the rails session instead to store the variable.
As a rule of thumb, it seems like you should NOT use global variables nor class variables in rails if your variables are changing.
The values stored in the session object are the same from any process in rails. Though the pointer to the session may be different, the values will remain the same. Here's another post about sessions that goes more into detail:
How is rails session shared among unicorn workers?
Im using a Rails engine as a cms. It all works fine. Im adding devise to this.
My generated devise controllers inherit from Devise::SessionsController. But there are some filters that are run from another controller in the engine that wont run in this case. A lot of the site relies on these filters being run. Of course I could just duplicate them, but thats bad juju.
So my Question is: How can I make one controller run the filters from another? I would prefer not to edit either of the gems.
Multiple inheritance is not supported in Ruby. I think extracting the filters into a module and mixing them in would be the cleanest solution.
See for example:
http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_modules.html
There is another option available for Devise: config.parent_controller. It defaults to ApplicationController but can be changed to something else. In my case (a Rails API) I use ApiController. In config/initializers/devise.rb:
Devise.setup do |config|
# ... other configuration options
config.parent_controller = 'ApiController'
end