I am making a gem https://github.com/BDMADE/college-admin , It is a simple a layout of an admin template, I am making this gem, for why, I want to re-use in my several rails app.
My welcome controller:
class WelcomeController < ApplicationController
layout 'college-admin/main'
def index
#hello = 'Hello Word'
end
end
My views:
<h1>Welcome#index</h1>
<p>Find me in app/views/welcome/index.html.erb</p>
<%= #hello %>
But when I call from my welcome controller of a demo project, it does shows this error.
So that, My question is how to use my layout(which is laid in college-admin gem) in this controller ?
Should I any change in college-admin gem to display it's layout ?
You need an engine which is a gem with added integration in to the Rails stack.
Engines can be considered miniature applications that provide
functionality to their host applications. A Rails application is
actually just a "supercharged" engine, with the Rails::Application
class inheriting a lot of its behavior from Rails::Engine.
You can create a new engine with:
bundle exec rails plugin new <NAME> --mountable --full --dummy-path=spec/dummy
I got the solution:
Just use this on lib/college/admin.rb:
require "college/admin/engine"
before:
require "college/admin/version"
module College
module Admin
# Your code goes here...
end
end
After the change:
require "college/admin/version"
require "college/admin/engine"
module College
module Admin
# Your code goes here...
end
end
From the controller:
layout 'main'
Related
I'm working on a gem that does some general string manipulations I'd like to expose as helper methods to rails 4+ apps.
Not all consumers of my gem are rails apps so i'd like to safely expose helper methods to rails apps.
Two questions:
How do I add view helper methods to Rails from a gem and where should it live within the gem directory structure?
What can i do to prevent a blow up when the consumer is NOT a rails app? i.e. the gem can't find rails when it's included
Thanks
In your lib/my_gem.rb, you typically want to do something along these lines:
require 'my_gem/action_methods' if defined? ActionView
And lib/my_gem/action_view_methods.rb would contain all if your methods that require Rails/ActionView.
You can add these helpers to Rails with:
module ActionMethods
# ...
end
ActionView::Base.send :include, ActionMethods
Also see this question, and this one.
The rails way is by creating an engine and as it gets loaded with your gem it gets processed by rails
module MyGem
class Engine < ::Rails::Engine
isolate_namespace MyGem
initializer "my_gem.include_view_helpers" do |app|
ActiveSupport.on_load :action_view do
include MyViewHelper
end
end
end
end
Another way you can go is to not include the helper by default so that consumers don't get unexpected side-effects from your gem. Create the helper as a module and document that it should be added to ApplicationController or any needed controller.
I am building a spree shop into an exiting Rails application, and I need to access link_to_cart from outside of the Spree engine.
link_to_cart can be found here: spree/core/app/helpers/spree/base_helper.rb
Since I have modified the styling in link_to_cart, I also created:
#helpers/spree/base_helper_decorator.rb
module Spree
module BaseHelper
def link_to_cart(text = nil)
text = text ? h(text) : Spree.t('cart')
css_class = nil
if simple_current_order.nil? or simple_current_order.item_count.zero?
text = "#{text}: (#{Spree.t('empty')})"
css_class = 'empty'
else
text = "<i class='fa fa-shopping-cart'></i> #{text}: (#{simple_current_order.item_count}) <span class='amount'>#{simple_current_order.display_total.to_html}</span>".html_safe
css_class = 'full'
end
link_to text.html_safe, spree.cart_path, :class => "cart-info #{css_class} btn btn-small btn-success pull-right", style: "margin-left:10px;"
end
end
end
I have tried doing stuff like Spree::BaseHelper.link_to_cart outside of the engine, but I keep getting undefined local variable or method 'link_to_cart'
I found this on a different StackOverflow question, and it seems promising, but I'm not sure how to modify it for my needs:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.action_controller' do |app|
ActiveSupport.on_load :action_controller do
helper MyEngine::ImportantHelper
end
end
end
end
Ok, thanks Ben for getting me on the right track. Here was my solution:
# class ApplicationController < ActionController::Base
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::Auth
helper Spree::BaseHelper
helper Spree::StoreHelper
Update
I ran into an issue with current_store being undefined outside of the engine. I'm not sure how to solve this properly, but in the meantime I've just added the following to stop spree from calling current_store:
module Spree
module Core
module ControllerHelpers
module Order
def current_order_params
{ currency: current_currency, guest_token: cookies.signed[:guest_token], store_id: Spree::Store.first, user_id: try_spree_current_user.try(:id) }
end
end
end
end
end
Also helper Spree::StoreHelper seems to no longer be required to display the cart button and current orders..
You need to do two things
Create your override
Make your override accessible within the main
app
Step 1: My Spree overrides are in the overrides folder in my main app. The override should call module_eval on the module that you are decorating.
#overrides/base_helper_decorator.rb
Spree::BaseHelper.module_eval do
def link_to_cart(text = nil)
#Your customizations here
end
end
Step 2: Add one of the lines below to your main app ApplicationController to access your decorated helper.
helper_method :link_to_cart # Add only the link_to_cart method
helper 'spree/base' # Add all methods (your decoration plus the default methods)
# from the Spree BaseHelper module to your main app
There have been some updates since Abram left his answer. It got me on the right track but I had a few hiccups. The main one was current_currency being undefined.
application_controller.rb
class ApplicationController < ActionController::Base
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::Auth
include Spree::Core::ControllerHelpers::Store
include Spree::Core::ControllerHelpers::Common
helper Spree::BaseHelper
I overrode the Spree navbar and used the one for my main app.
It started working when I added include Spree::Core::ControllerHelpers::Common. Unfortunately, this renders all views through the spree spree_application.html.erb layout. You may have to override and tinker a bit with this view.
Also, all of the css comes from spree at this point. You'll have to move your custom css into the spree namespace and #import it.
You will have to include the right helpers in your main application outside the engine too. In the upcoming version 3.2.0 of spree I just had to add:
# class ApplicationController < ActionController::Base
include Spree::Core::ControllerHelpers::Store
include Spree::Core::ControllerHelpers::Order
include Spree::Core::ControllerHelpers::Auth
to my ApplicationController to make link_to_cart work everywhere. You'll need to include more then one helper class, because for example current_store (used by other helpers) is defined in Spree::Core::ControllerHelpers::Store, so add that too to get rid of any errors.
You can add this to your main_app to access the cart.
<%= link_to "Cart", spree.cart_path %>
I'm making a gem for Rails. I need access to the ApplicationController because I'll toy with it. Absolutely nothing online gives information about what to do with gemspec and then somehow manage to get Rails accessible in my gem.
I imagine the goal is eventually to be able to talk to Rails like:
module Rails
module ActionController
#code
end
end
If you are developing a gem exclusively for Rails I strongly recommend you generate the initial scaffold using rails plugin new gem_name. There's a ton of info on developing rails plugins.
The initial structure generated looks like this:
gem_name
gem_name.gemspec
lib/
gem_name.rb
gem_name/
version.rb
engine.rb # if generated using --mountable
The whole rails environment becomes available [edit: after your gem is loaded] so extending ApplicationController can be done like this:
# lib/gem_name.rb
require 'gem_name/controller_extensions'
module GemName
end
# lib/gem_name/controller_extensions.rb
module GemName::ControllerExtensions
# bleh
end
# dummy_application/app/application_controller.rb
class ApplicationController < ActionController::Base
include GemName::ControllerExtensions
end
Look at this question.
I have found a few articles addressing the issue of helpers within an engine not being accessible to the consuming (parent) application. To make sure we are all on the same page, let's say we have this:
module MyEngine
module ImportantHelper
def some_important_helper
...do something important...
end
end
end
If you look at the rails engine documentation in the "Isolated engine's helpers" (L293), it says:
# Sometimes you may want to isolate engine, but use helpers that are defined for it.
# If you want to share just a few specific helpers you can add them to application's
# helpers in ApplicationController:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::SharedEngineHelper
# end
#
# If you want to include all of the engine's helpers, you can use #helpers method on an engine's
# instance:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::Engine.helpers
# end
So if I ask anybody consuming my engine to add this to their application_controller.rb, then they will get access to all my important helper methods:
class ApplicationController < ActionController::Base
helper MyEngine::ImportantHelper
end
This is what I want and it works, but that's kind of a pain, especially if, as is my use case, everything the engine exposes can/should be used anywhere in the consuming app. So I dug around a bit more and found a solution that suggested I do the following:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ImportantHelper)
end
end
end
Now this is exactly what I want: to add all my ImportantHelper method to the parent app's application helper. However, it doesn't work. Can anybody help me figure out why this more-better solution does not work?
I am running ruby 1.8.7 with rails 3.1.3. Let me know if I missed any important info germane to the issue, and thanks in advance.
You can create an initializer to accomplish this like so:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.action_controller' do |app|
ActiveSupport.on_load :action_controller do
helper MyEngine::ImportantHelper
end
end
end
end
I have written two blog posts about creating engines from scratch, and following them everything should work as expected (without additional configurations needed). Maybe you are still interested in:
Rails 3.1 Engines: Part I – The engine
Rails 3.1 Engines: Part II – The gem
Rails 3.1 Engines: Part III – The environment
Update: It's three articles in the meantime, and there's still some info to come. Please give me feedback.
If you want to keep the code in the engine, instead of every implementing application, use this:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
MyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
module YourEngine
module Helpers
def a_helper
end
...
end
end
ActionController::Base.send(:helper, YourEngine::Helpers)
Include this code in engine.rb is also be very helpful
config.before_initialize do
ActiveSupport.on_load :action_controller do
helper MyEngine::Engine.helpers
end
end
Basically your engine would look like
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
# Here comes the code quoted above
end
end
I am working on a rails engine and I have a problem with the helpers.
Apparently this is a known "problem" but there's not a lot of solutions out there. The problem is that I have an AuthenticationHelper which I want to access globally - but it's not working.
I've read that you could add a few lines to your init.rb but it does not seem to have any effect.
Any idea what the best way to make an application available in an engine?
EDIT: Fixed it- Just put the code (from the link) in the engine.rb instead.
Put this code in engine.rb:
config.to_prepare do
ApplicationController.helper(MyEngineHelper)
end
To access main app helpers (ApplicationHelper) from engine's views I tried include this:
app/helpers/your_engine/application_helper.rb
module YourEngine
module ApplicationHelper
include ActionView::Helpers::ApplicationHelper
end
end
It works, but once, when I restarted dev server, it throws me uninitialized constant ActionView::Helpers::ApplicationHelper, but I can't reproduce this exception.
EDIT
Removed this include and made this one:
lib/my_engine/engine.rb (it's inside engine)
module MyEngine
class Engine < ::Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ActionView::Helpers::ApplicationHelper)
end
end
end
Adding this just in case:
I had the same problem using the Administrate gem with Rails 7. I wanted to access my main app helper modules.
Simply adding helper all_helpers_from_path 'app/helpers' in Admin::ApplicationController solved this. You can find the official documentation here.
My file now looks like this:
module Admin
class ApplicationController < Administrate::ApplicationController
before_action :authenticate_admin_user!
helper all_helpers_from_path "app/helpers"
end
end
I found the answer here.