I am working on an old plugin "menu_helper" (legacy code uses it).
https://github.com/pluginaweek/menu_helper
The main entrance of this library is as follows,
module PluginAWeek
module MenuHelper
def menu_bar(options = {}, html_options = {}, &block)
puts #controller.class
MenuBar.new(#controller, options, html_options, &block).html
end
end
end
ActionController::Base.class_eval do
helper PluginAWeek::MenuHelper
end
The code works in rails 2.3.5 without problem but fails in 4.2.6.
When I puts #controller.class, in 2.3.5, it will always return the current controller that is using this library, but in 4.2.6 it will be NillClass.
So where does this #controller come from? How do I modify in 4.2.6 to make it work.
Note 1: to use this, I just need to call
html = menu_bar(options,:id => 'menuid')
No any controller is passed in.
Note 2: I am currently running it on controller test.
Thanks.
First of all, I would not use a gem that has not been maintained in the past 5 years and that the master build is currently failing. I'd try to find a well-maintained alternative or if the gem is small enough, to redo it myself.
This being said, menu_helper seems to use this variable: https://github.com/pluginaweek/menu_helper/blob/master/lib/menu_helper/menu.rb#L51
If you want to make it work, do a before_action that would instantiate this variable with the current controller:
before_action :set_legacy_controller
def set_legacy_controller
#controller = controller
end
Related
I want to use dynamic path in my controller in rails gem.
I've added to
module MyGem
class FooController < Config.controller
before_action ->{ append_view_path "app/views/my_gem/#{wizard_name}" }
...
and in views I need to specify path like
app/views/my_gem/#{wizard_name}/my_gem/foo/some.erb
is in Rails some way, to cut the relative path of gem namespace, and get lookup path like?
app/views/my_gem/#{wizard_name}/some.erb
# or
foo/bar/some.erb
Thank you!
UPD:
I understand, that there is way with disabling isolate_namespace in Engine, but I believe, that it is not best and only option.
UPD2: For Rails4 this idea was very useful https://coderwall.com/p/p_yelg/render-view-outside-of-a-controller-in-rails-4
The Devise gem has a way of adding views lookup path by overriding the _prefixes method:
class DeviseController < Devise.parent_controller.constantize
include Devise::Controllers::ScopedViews
# Override prefixes to consider the scoped view.
# Notice we need to check for the request due to a bug in
# Action Controller tests that forces _prefixes to be
# loaded before even having a request object.
#
# This method should be public as it is is in ActionPack
# itself. Changing its visibility may break other gems.
def _prefixes #:nodoc:
#_prefixes ||= if self.class.scoped_views? && request && devise_mapping
["#{devise_mapping.scoped_path}/#{controller_name}"] + super
else
super
end
end
end
Can this be applied to your use case?
See source:
https://github.com/plataformatec/devise/blob/master/app/controllers/devise_controller.rb
Usually, you should only override the partial views or the functions from that gem, do not load from the gem lib like this, because when deploying to the real server it will raise many troubles for you to debug and improve.
You could make your how render method that reads an arbitrary file, interpret the ERB and render it as an HTML page for instance.
ERB.new(File.read(Rails.root.join('foo', bar', 'some.erb'))).result(binding)
By passing binding, the template will have access to the all the variables in the current context.
See ERB docs for more details: http://apidock.com/ruby/ERB
Assuming your gem is an engine, you should be able to simply call render :some in the engine. If the app has a view called <gem_name>/<controller_name>/some.html.erb it will be used.
Also, you can provide a version of that view in your gem that will be used if the app does not yet provide one.
If you need the wizard_name to also be looked up, I think the best way to do that would be to move that portion of the view path to the to the end where you are calling render.
So in your gem's controller you would write render "#{wizard_name}/some" and it would look for that view both in our app's app/views/<gem_name>/<controller_name>/<wizard_name>/some.html.erb and in your gem's app/views/<controller_name>/<wizard_name>/some.html.erb.
I have some logic that is going to manipulate data before starting a job queue. However, inside the controller and also in the rails console I cannot seem to access the classes. Example:
In app/services/hobo_service.rb I have
class HoboService
def initialize
#api = Hobos::Api.new
end
def run
hobo
end
private
attr_reader :api
def hobo
api.hobo
end
end
However, if in my relevent controller I put
...
def create
#name = HoboService.new.run
end
...
Raises an exception saying the object cannot be found.
It seems as if all in the app directory should be in the pipeline and available. What am I missing here? Haven't been on Rails since 3.2 until recently.
I'm not sure why a subdirectory of app would be ignored, but let's try the simple solution- what happens when you add this to the Application class in your application.rb?
config.autoload_paths += %W(#{config.root}/app/services)
Is it possible when using the MailView gem or Rails 4.1 mail previews to pass parameters into the MailView? I would love to be able to use query string parameters in the preview URLs and access them in the MailView to dynamically choose which record to show in the preview.
I stumbled upon the same issue and as far as I understand from reading the Rails code it's not possible to access request params from mailer preview.
Crucial is line 22 in Rails::PreviewsController (email is name of the mailer method)
#email = #preview.call(email)
Still a relevant question and still very few solutions to be found on the web (especially elegant ones). I hacked my way through this one today and came up with this solution and blog post on extending ActionMailer.
# config/initializers/mailer_injection.rb
# This allows `request` to be accessed from ActionMailer Previews
# And #request to be accessed from rendered view templates
# Easy to inject any other variables like current_user here as well
module MailerInjection
def inject(hash)
hash.keys.each do |key|
define_method key.to_sym do
eval " ##{key} = hash[key] "
end
end
end
end
class ActionMailer::Preview
extend MailerInjection
end
class ActionMailer::Base
extend MailerInjection
end
class ActionController::Base
before_filter :inject_request
def inject_request
ActionMailer::Preview.inject({ request: request })
ActionMailer::Base.inject({ request: request })
end
end
Since Rails 5.2, mailer previews now have a params attr reader available to use inside your previews.
Injecting requests into your mailers is not ideal as it might lead to thread safety issues and also means your mailers won't work with ActiveJob & co
In Rails, supposing that the file is already loaded, how it is possible to call my_method from this example from console?
# some_file.rb
class MyClass < ApplicationController::Base
def my_method(args)
Another, very simple way to do this is to use an instance of ApplicationController itself.
ApplicationController < ActionController::Base
def example
"O HAI"
end
end
Then in the console, you can do the following:
>> ApplicationController.new.example
This will output the following:
O HAI
This, of course, has the restriction of not having access to everything a normal request would, such as the request object itself. If you need this, as the Patrick Klingemann suggested, you could use the debugger... I personally recommend using Pry:
Pry on RubyGems.org
RailsCast: Pry with Rails
This is likely much too late for you, but hopefully it will help someone in the future.
use debugger:
in your Gemfile add:
gem 'debugger'
then from the terminal:
> bundle
> rails s --debugger
in the controller action you're hitting:
class WidgetsController < ApplicationController
def index
debugger
#widgets = Widget.all
respond_with #widgets
end
end
then point your browser to: http://localhost:3000/widgets, the page will not finish loading. Return to the terminal where your server is running and you'll be in an interactive debugging session where you can run: my_method
It is not exactly the question asked, but you can also debug with the pry gem, similarly to debugger.
Add to the Gemfile:
gem "pry"
gem "pry-remote"
gem "pry-stack_explorer"
gem "pry-debugger"
In your method:
def myMethod
binding.pry
# some code
end
Done!
When you run your method, the page processing will freeze at binding.pry and pry will take over the prompt. Type n for each new step of the method, and play around with your variables that can be print (just typing them) in "real-time"!
I'm trying to move an app to rails 3.1. Many of my tests break, because the submit buttons no longer have an id. the Release Notes (see sect. "5.3 - Action View") confirm it:
The submit form helper does not generate an id “object_name_id” anymore.
Here's the relevant commit that changed action_view/helpers/form_helper.rb.
I want have the old behaviour back without messing with the installed gem or changing all my views by hand. So I try to monkey patch it:
# this is config/initializers/FormHelperMonkeypatch.rb
module ActionView
module Helpers
module FormHelper # <-- this is the line phoet repaired, see his answer below
# code from rails 3.0
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
#template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
end
end
end
I restarted my server, but I see no effect of my patch. What am I doing wrong?
you are editing the wrong place. use this:
module ActionView
module Helpers
class FormBuilder
# code from rails 3.0
def submit(value=nil, options={})
value, options = nil, value if value.is_a?(Hash)
value ||= submit_default_value
#template.submit_tag(value, options.reverse_merge(:id => "#{object_name}_submit"))
end
end
end
end