Rails override controller layout in gem - ruby-on-rails

I am using a gem that adds an engine to my Rails app with routes that all render snippets of html using the default application layout. I want the controller in this gem to use a different layout. Is there a way I can add code to an initializer that will dynamically cause a controller in a gem to always use a layout. E.g. can I throw something like the following code (which isn't working) in an initializer assuming the full definition of SampleController is defined in the gem I am using?
class SampleController
layout 'my_layout'
end
I know you can call class_eval or instance_eval for class and instance methods but how to override this type of initialization code?

If you create a 'sample.html.haml' (or whichever templating language) in your layouts directory, it will be used instead of application.html for SampleController.
http://guides.rubyonrails.org/layouts_and_rendering.html#finding-layouts

Related

When two Rails engines define a method of the same name, which one overrides

If a Rails app has two engines that define the same method, which version "wins"? How is it decided which one gets executed when the method's name is evoked?
For example, both the Rails engine blacklight and another Rails engine meant to modify Blacklight's functionality define a method like this:
module Blacklight
class SuggestSearch
def suggestions
...
end
end
end
The method definition is the same in the two engines but with ... in place of different content. The second of the engines is meant to override the functionality of the first.
The two engines are used in the same app, like this:
gem 'blacklight'
gem 'trln_argon'
How does rails know which engine modifies the other? How is the second engine's definition of suggestions made to prevail?

Rails gem: correct place for a controller helper?

I'm working on a private gem for Rails which includes a controller helper.
I doubt between these two places for this helper:
lib/gem_name/my_helper.rb
app/controllers/concerns/gem_name/my_helper.rb
What is the correct place for it?
I think you should use it in controller concern, then include helper module into Application controller
So I will prefer app/controllers/concerns/gem_name/my_helper.rb for creating controller helper inside gem.

Can't use cells from an engine inside Rails application

I created an engine which provides an ui component as a cell. The corresponding gem (criteria_operator-ui_component) contains nearly no code inside the lib folder, because for cells to function properly I had to work inside the assets path. The base file of the gem looks like this:
require 'criteria_operator/ui_component/engine'
require 'cells/rails'
module CriteriaOperator
module UiComponent
# Your code goes here...
end
end
The engine doesn't contain much, either:
module CriteriaOperator
module UiComponent
class Engine < ::Rails::Engine
require 'jquery-rails'
require 'criteria_operator'
isolate_namespace CriteriaOperator::UiComponent
end
end
end
To me, it looks like the gem couldn't even know about the cell, but as far as I know I'm not allowed to include anything from outside the lib folder. Also, testing the cell in the dummy application within the project is working fine.
Now I'm using this engine inside a real Rails application. In the gemfile, I included the following:
gem 'criteria_operator'
gem 'cells' # i added these three, because `bundler list` didn't show me
gem 'cells-rails' # `cells-rails` and `cells-erb` even though they are listed
gem 'cells-erb' # as dependencies for the engine
gem 'criteria_operator-ui_component'
I mounted the routes
mount CriteriaOperator::UiComponent::Engine => '/criteria_operator-ui_component'
and tried using the cell CriteriaOperator::UiComponent::CriteriaEditor like I did in the dummy application. Inside erb:
cell('criteria_operator/ui_component/criteria_editor', #op)
or from code:
include Cell::RailsExtensions::ActionController
def whatever
cell(CriteriaOperator::UiComponent::CriteriaEditor, #op).call()
end
The error is ActionView::Template::Error (uninitialized constant CriteriaOperator::UiComponent::CriteriaEditor).
What am I doing wrong? Am I just missing something when using the engine, or is the engine itself implemented the wrong way? And if that's the case, why does the dummy application work? I'm totally stuck, this is my first time creating a Rails Engine as well as my fist time working with cells...
The full code of the engine (including the dummy application) can be found on GitHub (this isn't supposed to be any advertisement, it's just in case anyone needs additional information).
You're calling CriteriaOperator::UiComponent::CriteriaEditor but that class/module does not seem to exist.
CriteriaOperator::UiComponent::Engine works OK because it's defined in the engine itself.
I'm guessing that your sample application works because it's using the view-based invocation like cell('criteria_operator/ui_component/criteria_editor') which presumably works with the javascript? You can't use the "code" version without defining the cell as a class like this:
https://github.com/trailblazer/cells#cell-class

How to Include a Helper Method from a Rails Engine (Spree) inside of another Rails Engine (AlchemyCMS)

What I've run into is this:
AlchemyCMS is a Rails Engine for allowing Rails applications to have a Content Management System. It also has a preview page where it can load up an iframe of the example page with the layout. The layout here is the Spree layout. I've modified Alchemy to be able to load up the spree application layout and not its default.
In doing so, it is not loading up the helper methods. I am currently receiving:
undefined local variable or method `title' for #<#<Class:0x007f8dcc359498>:0x007f8de17dd6a8>
Where title is the first helper method in the application.
I've tried 5000 different techniques to try to load in Spree's helper methods into AlchemyCMS and I just can't do it.
Does anyone know how?
Ben,
You could do so by either including Spree's helpers within your application controller or within the base Alchemy controllers.
There is an extension for alchemy and spree together, which does a similar thing here:
https://github.com/magiclabs/alchemy_spree/blob/master/app/controllers/spree/base_controller_decorator.rb
You will just want to go in the opposite direction so instead of decorating a Spree controller to add Alchemy in you would decorate Alchemy controllers to include whichever of Spree's controller helpers you need to use:
https://github.com/spree/spree/blob/master/core/app/controllers/spree/base_controller.rb
In this case you need to include the common controller helpers:
https://github.com/spree/spree/blob/master/core/lib/spree/core/controller_helpers/common.rb
EDIT:
Alchemy::BaseController.class_eval do
include Spree::Core::ControllerHelpers
include Spree::Core::ControllerHelpers::Store
helper Spree::Core::Engine.helpers
end

Access Main App Helpers when overridings a Rails Engine View/Layout

I have created a simple Rails Engine to provide some general functionality(photo gallery) to an application. I want to be able to override the standard _header partial so that the menu for the gallery matches that of my main application. In my header view I call a helper that is part of application_helpers (main app), but I keep getting "undefined method" errors. From what I can tell the main app application_helpers are not being included (obviously) when I override the engines application layout or its partials.
So my question is, how do I override an engine view in the main application, and get access to the main application helper methods? I would still need access to the engine helpers as well as not to screw up the engine functionality.
Do I need to override the controllers as well? seem like a lot just to get some helpers.
Thanks
Rails 3.1.3
check out this blog post: http://www.candland.net/2012/04/17/rails-routes-used-in-an-isolated-engine/
The author adds a method_missing definition to the application helper in order to access the parent application's helpers.
/config/initializers/blogit.rb
module Blogit
module ApplicationHelper
def method_missing method, *args, &block
puts "LOOKING FOR ROUTES #{method}"
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
if main_app.respond_to?(method)
main_app.send(method, *args)
else
super
end
else
super
end
end
def respond_to?(method)
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
if main_app.respond_to?(method)
true
else
super
end
else
super
end
end
end
end
Try including the main app helper methods. For instance:
class MyEngineClass
include ApplicationHelper
#...
end
You may possibly need to require the file first, though I would expect Rails to correctly find it in this case.
Once ApplicationHelper is included, you should be able to directly use those helpers in the controller.
It also looks like you can call ClassName.helper("application") for a lot of Rails classes -- not sure if that will work here.
try creating a helper in your application with the same name of the helper in your engine in order to override engine helper methods.
I found this discussion particularly insightful. There are also some interesting ideas in the Rails Engine API docs under Isolated engine helpers.
Engines are supposed to be independent from the main app, that is why you can't access its helpers from the Engine.
However, there are hack-ish ways for giving your engine access to the helpers of the main app. This is how I did it:
# In the main app
# initializers/share_helpers_path_with_engine.rb
PhotoGallery::Engine.class_eval do
paths["app/helpers"] << File.join(File.dirname(__FILE__), '../..', 'app/helpers')
end
You need of course to change PhotoGallery to the actual name of your engine class.
Feel free to take a look at the Engines documentation (section about the paths): http://edgeapi.rubyonrails.org/classes/Rails/Engine.html
Disclaimer: I've only used this solution in Rails 3.2 engines.
If, in your engine you have a standard header partial vendor/gems/my_gallery_engine/app/views/application/_header.html.erb.
Then, override it in your main app by creating a customized partial app/views/application/_header.html.erb.
The override works because Rails' view template search path (by default) starts with the main apps' app/views directory, and then searches through engines' app/views in load order.
All of your main app's Helpers will be available in the partial.

Resources