My app should render html, to answer when a user clicks ajax-link.
My controller:
def create_user
#user = User.new(params)
if #user.save
status = 'success'
link = link_to_profile(#user) #it's my custom helper in Application_Helper.rb
else
status = 'error'
link = nil
end
render :json => {:status => status, :link => link}
end
My helper:
def link_to_profile(user)
link = link_to(user.login, {:controller => "users", :action => "profile", :id => user.login}, :class => "profile-link")
return(image_tag("/images/users/profile.png") + " " + link)
end
I have tried such methods:
ApplicationController.helpers.link_to_profile(#user)
# It raises: NoMethodError (undefined method `url_for' for nil:NilClass)
and:
class Helper
include Singleton
include ActionView::Helpers::TextHelper
include ActionView::Helpers::UrlHelper
include ApplicationHelper
end
def help
Helper.instance
end
help.link_to_profile(#user)
# It also raises: NoMethodError (undefined method `url_for' for nil:NilClass)
In addition, yes, I KNOW about :helper_method, and it works, but i don't want to overload my ApplicationController with a plenty of that methods
helpers are just ruby modules which you can include in any controller just like any module.
module UserHelper
def link_to_profile(user)
link = link_to(user.login, {:controller => "users", :action => "profile", :id => user.login}, :class => "profile-link")
return(image_tag("/images/users/profile.png") + " " + link)
end
end
And, in your controller :
class UserController < ApplicationController
include UserHelper
def create
redirect_to link_to_profile(User.first)
end
end
Oki. Let's recap. You want access to certaint functions/methods, but you don't want those methods to be attached to current object.
So you want to make a proxy object, that will proxy/delegate to those methods.
class Helper
class << self
#include Singleton - no need to do this, class objects are singletons
include ApplicationHelper
include ActionView::Helpers::TextHelper
include ActionView::Helpers::UrlHelper
include ApplicationHelper
end
end
And, in controller:
class UserController < ApplicationController
def your_method
Helper.link_to_profile
end
end
The main disadvantage to this approach is that from the helper functions you won't have access to controller context (EG you won't have access to params, session, etc)
A compromise would be to declare those functions as private in the helper module, therefore, when you will include the module, they will also be private in the controller class.
module ApplicationHelper
private
def link_to_profile
end
end
class UserController < ApplicationController
include ApplicationHelper
end
, as Damien pointed out.
Update: The reason why you get the 'url_for' error is that you do not have access to controller's context, as stated above. You could force passing the controller as a parameter(Java-style ;) ) like:
Helper.link_to_profile(user, :controller => self)
and then, in your helper:
def link_to_profile(user, options)
options[:controller].url_for(...)
end
or event a bigger hack, presented here. However, i would reccomend the solution with making methods private and including them in the controller.
Take that! http://apotomo.de/2010/04/activehelper-rails-is-no-pain-in-the-ass/
That's exactly what you were looking for, dude.
Related
I have and active admin resource. How i can dynamic extend resource. I try do it like this:
ActiveAdmin.register Order do
include UpdatePriceBlock
price_blocks_names names: [:last, :actual]
end
module UpdatePriceBlock
extend ActiveSupport::Concern
def price_blocks_names(options = {})
#price_blocks_names ||= options[:names]
end
def self.included(base)
#price_blocks_names.each do |name|
base.send :member_action, name, method: :get do
end
end
end
end
Now I has an error:
undefined method `price_blocks_names' for #<ActiveAdmin::ResourceDSL
This is a possible way, I don't know yet how you could keep the names inside the active admin register block. Add the price_blocks_names to your model:
class Order < ApplicationRecord
def self.price_bloks_names
%i(last actual)
end
end
And then place this in config/initializers/active_admin_update_price_block.rb
module ActiveAdminUpdatePriceBlock
def self.extended(base)
base.instance_eval do
self.controller.resource_class.price_bloks_names.each do |name|
member_action name, method: :get do
raise resource.inspect
end
end
end
end
end
Now you can extend, but the configuration needs to reside in the model as a class method this way. Haven't found a cleaner way so far.
ActiveAdmin.register Order do
extend UpdatePriceBlock
end
I think I found it:
ActiveAdmin.register Order do
controller do
include UpdatePriceBlock
end
end
What's going on:
Within the register Order do block, self is a special Active Admin thing:
ActiveAdmin.register Order do
puts "What's self here? #{self}"
end
=>
What's self here? #<ActiveAdmin::ResourceDSL:0x000000012b948230>
Within the controller do block, it's the controller class (so, pretty much the same as the body of a class definition):
ActiveAdmin.register Order do
controller do
puts "What's self here? #{self}"
include UpdatePriceBlock
end
end
=> What's self here? Admin::OrdersController
Within a member_action block, it's an instance of the controller, just like in a regular Rails controller action:
ActiveAdmin.register Order do
member_action :action do
puts "What's self here? #{self}"
end
end
=> What's self here? #<Admin::OrdersController:0x00000001259e7e80>
I'm doing an authentication application. I have this code
class UsersController < ApplicationController
def new
#user = User.new
#title = "User Sign Up"
end
def create
#user = User.new(params[:user])
sign_in_check #user
if #user.save
#flash[:status] = true
#flash[:alert] = "You have successfully signed up!!"
#sign_in_check #user
redirect_to root_path, :flash => { :success => "Welcome to the Bakeshop"}
else
#title = "User Sign Up"
render 'new'
end
end
end
This is a simple sign-up code, and whenever I try and sign up, rails returns an error:
undefined method `sign_in_check' for #<UsersController:0x68c0a90>
but I defined a method sign_in_check in my Users_helper.rb:
module UsersHelper
def sign_in_check(user)
#some stuff to enable session
end
end
Does anyone have an idea why this is happening, and how to fix it?
The reason is your method is a helper. Helpers will be available in views with matching name by default, but not open to controllers without setting.
Two ways to fix:
Allow this helper in UsersController
class UsersController < ApplicationController
helper :user #This will expose UsersHelper module to UsersController
Instead, put this method into ApplicationController. I would prefer this due to the method's nature.
Include your UserHelper in your UserController as follows and you should be able to use any methods defined within the helper.
class UsersController < ApplicationController
include UsersHelper
...
end
This is usually put in the application controller
class ApplicationController < ActionController::Base
def sign_in_check(user)
#some stuff to enable session
end
end
Helpers are used for views. If you want to use it in both - you can do that, but that doesn't sound like what you're looking for here.
just include your helper module in your controller
class UsersController < ApplicationController
helper :user
...
end
Thanks
orry for the basics, but I'm having a hell of a time getting a very basic flow to work:
1) Define module with a method to save a url to a var (or return it)
2) Call that method in a controller to initialize the method
3) Have a view show that URL
NoMethodError in AuthController#oauth undefined method `oauthurl' for GetAccessToken:Module
Module: \lib\get_access_token.rb
module GetAccessToken
CONSUMER_TOKEN = { :token=>"mytokenstringwhichisreallylong", :secret=> "mysecretstringwhichisreallylong" }
def self.oauthurl
#oauthurl="https://us.etrade.com/e/t/etws/authorize?key=#{(CONSUMER_TOKEN[:token])}&token="
end
end
Controller: app\controllers\auth_controllers.rb
require 'get_access_token'
class AuthController < ApplicationController
include GetAccessToken
before_filter :oauthurl1
def oauthurl1
GetAccessToken.oauthurl
end
end
View: app\views\auth\oauth.html.erb
<% provide(:title, 'oAuth') %>
<h1>oAuth</h1>
<%= link_to "oAuth", #oauthurl %>
My higher level goal is to get the eTrade oAuth flow working, but I want to make sure I understand every line of code vs. taking someone else's and I can't get this very basic building block to work yet.
Add the following to config/application.rb:
config.autoload_paths += Dir["#{config.root}/lib/**"]
Change your AuthController to:
class AuthController < ApplicationController
include GetAccessToken
def oauthurl1
GetAccessToken.oauthurl
end
end
Your module code will be
module GetAccessToken
CONSUMER_TOKEN = { :token=>"mytokenstringwhichisreallylong", :secret=> "mysecretstringwhichisreallylong" }
def self.oauthurl
"https://us.etrade.com/e/t/etws/authorize?key=#{(CONSUMER_TOKEN[:token])}&token="
end
end
and your controller code should be
require 'get_access_token'
class AuthController < ApplicationController
include GetAccessToken
def oauthurl1
#oauthurl = GetAccessToken.oauthurl
end
end
We need to initialize #oauthurl in the controller to use this variable in the view, else it will be nil.
With the generous help of the contributions above, this is how I finally resolved the error. I defined the instance variable in the controller instead of the model, and initialized the controller method using before_filer:
Model:\lib\test_module.rb
module TestModule
CONSUMER_TOKEN = { :token=>"myReallyLongToken", :secret=> "myReallyLongSecret" }
def self.testUrl
"https://us.etrade.com/e/t/etws/authorize?key=#{(CONSUMER_TOKEN[:token])}&token="
end
end
Controller: app\controllers\test_controller.rb
require 'test_module'
class TestController < ApplicationController
include TestModule
before_filter :testUrl1Init
def testUrl1Init
#testurl=TestModule.testUrl
end
end
View: \app\views\test\test.html.erb
<% provide(:title, 'test') %>
<h1>test</h1>
<%= link_to "test link", #testurl %>
The respond_with accepts some parameters, e.g. respond_with(#resource, methods: [:method])
These options should be used in every action. So instead of putting it into every method by hand, is there a possibility to set some default options for just this controller?
The easy and customizable way to do this is by creating a new response method that wraps responds_with.
For example:
class ResourcesController < ApplicationController
def index
#resources = Resource.all
custom_respond_with #resources
end
private
def custom_respond_with(data, options={})
options.reverse_merge!({
# Put your default options here
:methods => [ :method ],
:callback => params[:callback]
})
respond_with data, options
end
end
You could, of course, also overwrite the respond_with completely, however, I find it to be much clearer in the code if you change the name of the method. It also will allow you to use a custom_respond_with in most actions but the standard respond_with in one or two if necessary.
Taking this one step further, if you move the custom_respond_with method to the ApplicationController, you can use it in all of your controllers as necessary.
If you want to specify different default options on a per controller basis, you can do so easily:
class ResourcesController < ApplicationController
def index
custom_respond_with Resource.all
end
private
def custom_respond_options
{ :methods => [ :method ] }
end
end
class ApplicationController < ActionController::Base
protected
def default_custom_respond_options
{}
end
def custom_respond_with(data, options={})
options.reverse_merge! default_custom_respond_options
respond_with data, options
end
end
Noob scoping issue, I imagine. :\
class ApplicationController < ActionController::Base
protect_from_forgery
#locations = get_locations
def get_locations
Location.where(:active => true).order('name').all
end
end
Error:
undefined local variable or method `get_locations' for ApplicationController:Class
Two questions:
1) What's with the error? Am I calling the method incorrectly?
2) How do I access this method from a sub-classed controller?
You're calling get_locations within the class scope, but the method is an instance method, not a class method. If for example you used def self.get_locations then you would be providing a class method, one of which you can use within the class scope (after you have defined it, not before like you're doing).
The problem here is the logic, what is this method for? What do you intend to use #locations for? If it's to go inside your application view, then you should put this method into the ApplicationHelper module, and call it from inside the relevant action. If you'd like it in another view on another controller and you'd like to use #locations inside your locations method, perhaps your setup might look something like this:
PagesController
class PagesController < ActionController::Base
def locations
#locations = Location.where(:active => true).order('name').all
end
end
locations.html.erb
<% #locations.each do |location| %>
<%= # do something with 'location' %>
<% end %>
If you'd like to use this inside of your application.html.erb you can simplify it quite some..
ApplicationController
class ApplicationController < ActionController::Base
protect_from_forgery
def locations
Location.where(:active => true).order('name').all
end
end
application.html.erb
<% locations.each do |location| %>
<%= # do something with location %>
<% end %>
The answer boils down to logic, and to really figure out exactly what you're looking for, more details would probably be required.
You're calling it from the class scope, not from an instance scope. more likely what you want is the following:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :setup_locations
private
def setup_locations
#locations = Location.where(:active => true).order('name').all
end
end
To make your original example work, you'd need to make #get_locations defined on self (which points to the class at definition), like so:
class ApplicationController < ActionController::Base
protect_from_forgery
#locations = get_locations
def self.get_locations
Location.where(:active => true).order('name').all
end
end
The problem with that code is that #locations will only be available from the class level as a class instance variable, which is comparable to a static variable in most other languages, and which probably isn't what you want.
I imagine that this line:
#locations = get_locations
... is trying to access the class level method get_locations and not the instance method.
The clue here is that the error message is showing that it can't find it on the class itself (ApplicationController:Class) and not an instance of that class. That means that you're in the class scope, not instance scope.
This would fix it:
def self.get_locations
Location.where(:active => true).order('name').all
end
Even the question is quite old, you can also call your controller action anywhere just by calling:
ApplicationController.new.get_locations