Rails 4.1
Ruby 2.0
Windows 8.1
In my helpers/application_helper.rb, I have:
def agents_and_ids_generator
agents = Agent.all.order(:last)
if agents
agents_and_ids = [['','']]
agents.each do |l|
name = "#{l.first} #{l.last}"
agents_and_ids << [name,l.id]
end
return agents_and_ids
end
end
In my views/agents/form.html.erb, I have the following:
<%= f.select :agent_id, options_for_select(agents_and_ids_generator) %>
In my controllers/agents_controller.rb, I have the following:
include ApplicationHelper
But when I go to this view, I get the following error message:
undefined local variable or method `agents_and_ids_generator' for #<#:0x00000006fc9148>
If I move the agents_and_ids_generator method to the helpers/agents_helper.rb, it works fine.
I thought that by putting methods in the application helper and including the application in a controller, then these methods are available to the views. Am I incorrect in that assumption?
Answer:
Making sure that application helper is not included in controllers, and added the following simplification:
<%= f.collection_select :agent_id, Agent.all.order(:last), :id, :name_with_initial, prompt: true %>
#app/models/agent.rb
Class Agent < ActiveRecord::Base
def name_with_initial
"#{self.first} #{self.last}"
end
end
Helpers
The bottom line answer is the application_helper is available in all your views.
Rails actually uses helpers all over the place - in everything from the likes of form_for to other built-in Rails methods.
As Rails is basically just a series of classes & modules, the helpers are loaded when your views are rendered, allowing you to call them whenever you need. Controllers are processed much earlier in the stack, and thus you have to explicitly include the helpers you need in them
Important - you don't need to include the ApplicationHelper in your ApplicationController. It should just work
Your Issue
There may be several potentialities causing the problem; I have two ideas for you:
Is your AgentsController inheriting from ApplicationController?
Perhaps your inclusion of ApplicationHelper is causing an issue
Its strange that your AgentsHelper works, and ApplicationHelper does not. One way to explain this would be that Rails will load a helper depending on the controller which is being operated, meaning if you don't inherit from ApplicationController, the ApplicationHelper won't be called.
You'll need to test with this:
#app/controllers/application_controller.rb
Class AgentsController < ApplicationController
...
end
Next, you need to get rid of the include ApplicationHelper in your controller. This only makes the helper available for that class (the controller), and will not have any bearing on your view
Having said this, it may be causing a problem with your view loading the ApplicationHelper - meaning you should definitely test removing it from your ApplicationController
Method
Finally, your method could be simplified massively, using collection_select:
<%= f.collection_select :agent_id, Agent.all.order(:last), :id, :name_with_initial, prompt: true %>
#app/models/agent.rb
Class Agent < ActiveRecord::Base
def name_with_initial
"#{l.first} #{l.last}"
end
end
Rails 5 update. I ran into a similar issue with views not picking up a method from application_helper.rb. This post helped me. The files that are provided in the helpers directory of a new rails app are for those views only. Methods in the application_helper.rb will not be available automatically to all views. To create a helper method that is available to all views, create a new helper file in the helper directory such as clean_emails_helper.rb and add your custom method here like this:
Module CleanEmailsHelper
def clean_email(email)
*do some stuff to email*
return email
end
end
Then you can call <%= clean_email(email) %> from any view in your app.
Related
I follow the complex nested forms in railscast and I made it work, a few tweaks here and there. But question is, how do I automatically load a nested form without using the controller?
def new
#model = Model.new
#model.child.build
end
many thanks!
how do I automatically load a nested form without using the
controller?
The simple (incorrect) answer is to include a Partial which will define the ActiveRecord objects as above, like this:
#app/views/controllers/_your_partial.html.erb
<% model = Model.new %>
<% mode.child.build %>
<%= form_for model do |f| %>
...
The problem with this is that it goes against the MVC programming pattern, which is the equivalent of God in the Rails world
--
MVC
Your question is rather peculiar, as it goes against one of the core elements of Rails:
This means every request you make to your Rails application (through the URL) will have to be catered for by a controller action. The role of the controller is to configure / collect the data required to run the app, and then populate the view for the user.
When you want to "call the form without a controller", you're basically saying you want to go against these conventions. Unless you create a gem (which will allow you to extrapolate the ActiveRecord definition code outside of your controller), you'll have to use the controller.
There is a trick though...
--
Class Method
You'll be able to take the functionality you're using in your controller & create a class method to handle it:
#app/models/model.rb
Class Model < ActiveRecord::Base
def self.build
model = self.new
model.child.build
model
end
end
This will allow you to call the following from your controller / partial:
#model = Model.build #-> instead of Model.new :)
module ApplicationHelper
def setup_person(person)
returning(person) do |p|
p.children.build if p.children.empty?
end
end
end
I found this on Ryan Daigle's site but I realize this is a bit outdated. I tried this but I get a returning is not defined or not a method.
I have a helper module for my home page with two methods that do the same thing:
module HomeHelper
def parsed_text(tweet)
auto_link (tweet).gsub(/(#\w+)/, %Q{\\1})
end
def other_parsed_text
self.auto_link.gsub(/(#\w+)/, %Q{\\1})
end
end
In my view this works:
<%= parsed_text(tweet.text) %>
But this doesn't:
<%= tweet.text.other_parsed_text %>
I get a NoMethodError at /
undefined method other_parsed_text. Isn't self the caller of the method inside of my helper method?
What am I doing wrong? I want the second style of calling methods with a . notation to work too. How do I do that?
This does not work because you didnt extend the class that tweet.text is of. You can use ActiveSupport::Concern if you want to extend some class. What you are doing now is provding some methods that can be called with parameters.
// I posted an example here: https://stackoverflow.com/a/8504448/1001324
More newbie issues.
I understand that if I define a method in my application helper, it is available to the entire app code.
In my applicaton helper, I have:
def primary_user_is_admin
if current_user
user_login_roles = JSON.parse(current_user.role)
if user_login_roles["admin"]
return 1
end
end
return nil
end
If I call it from the categories_controller:
if !primary_user_is_admin
redirect_to root_url
end
I get an error message: undefined local variable or method `primary_user_is_admin'
This also happens if I put the primary_user_is_admin code in the registrations_helper.rb file
However, if I use it in any of the views (views/user/edit.html.erb for instance)
<% if primary_user_is_admin %>
<% end >
then it works. What am I missing?
Helpers are not included into a controller by default. You can
include ApplicationHelper
To gain access to the methods in the ApplicationHelper module. The previous discussion has a bunch of useful solutions for accessing helpers in controller.
Methods defined in helpers are only available to views by default. You have to 'include ApplicationHelper' in the applications controller to get access to this method in the controllers.
I'm pretty new to Rails and have an issue which I can't quite get my
head around as to the architecturally 'correct' way of doing it.
Problem relates to what I kinda call sub-controllers. The scenario is
this:
I have a series of pages, on which is a panel of some form containing
some information (think the user panel on gitHub top right).
So, in my app, I have controllers that generate the data for the pages
and render out the responses which is fine, but when it comes to this
panel, it seems to me that you would want some sort of controller action
dedicated to generating this panel and it's view.
Question is, how do you go about doing this? How do I render a 'sub
controller' from within a view?
I would put the logic in a helper or a module. (http://api.rubyonrails.org/classes/ActionController/Helpers/ClassMethods.html)
Then render partials where you want these things displayed. (http://api.rubyonrails.org/classes/ActionView/Partials.html)
Like Herman said, if it's logic that you need generated after the controller hands off to the view (ie, the Pages controller generates a page view, but you want a customized panel) then put it in a helper. Or, call a separate method in your Pages controller before handing off to the view. Or, if it's a lot of logic, create a Module and stick it in your /lib folder. So you could have a whole Panel module with methods that generate different parts of your Panel and which are called by your controller. But if you want to call these methods from within the view, then you should use a helper instead.
I dont think a module is what is required here, modules are required for shared behaviour across a small subset of your classes.
What I think is required here is the understanding of the inheritance of ApplicationController and also layouts
so, for example, my layout might look like:
<html>
<head><title>Foo</title></head>
<body>
<%= render :partial => (current_user ? "/shared/user_widget_bar" : "/shared/login_bar") %>
<%= yield %>
</body>
</html>
Any code that i want to use for it would go in my ApplicationController since it would be shared across the majority of my app:
before_filter :generate_user_widget
def generate_user_widget
if current_user
#avatar = ...
#unread_messages = ...
end
end
I understand that it might be cleaner for it to belong in a separate controller BUT honestly, unless the code is huge, it doesn't matter and can even still be put inside a module which is then included by ActionController. However it does need to be inside ApplicationController if you consider the scope of it.
If there are more related pages, say for example, you have a Rails app that manages multiple sites and you want shared behaviour across a particular site, try creating a parent controller which has no actions and only private methods, any controllers that need to have access to those methods can inherit off it. That way you can apply before filters to all controllers which inherit off it, saving you the pain of forgetting to add one in your non-parent controllers.
e.g:
class SiteA::SiteAParentController < ApplicationController
before_filter :generate_user_widget
...
end
class SiteA::ProductController < SiteA::SiteAParentController
def index
...
end
end
well, if you really need to call a controller action from the view, you can use components. They were part of the framework, now they only exist as plugins. One such plugin that seems to be well maintained is here: http://github.com/cainlevy/components/tree/master
from its docs:
== Usage
Note that these examples are very simplistic and would be better implemented using Rails partials.
=== Generator
Running script/generator users details will create a UsersComponent with a "details" view. You might then flesh out
the templates like this:
class UsersComponent < Components::Base
def details(user_or_id)
#user = user_or_id.is_a?(User) ? user_or_id : User.find(user_or_id)
render
end
end
=== From ActionController
class UsersController < ApplicationController
def show
return :text => component("users/detail", params[:id])
end
end
=== From ActionView
<%= component "users/detail", #user %>
I currently have code in my ApplicationController to check if a user is logged in and has the required access to perform a given action (the tests take place in a before_filter).
I require the same functionality in the views to decide if I should be showing the admin links in a list view, but how do I best avoid duplicating code in the controllers and views?
The way I have chosen to do it now, is to have the user_can_edit_customers? essentially be a wrapper for 'can_edit_customers?' on my User class:
ApplicationController:
class ApplicationController
And then do something similar in my view helpers.
This way all functionality is encapsulated in the User model but I still need to define wrappers in my controllers and helpers, but are there smarter ways of doing this?
Note, the user stuff is only an example - this also applies to other pieces of functionality.
I would say do away with the wrappers and just call can_edit_customers? directly on the user object passed to the view.
If you want to keep them a solution might be to use helper_method in your controller.
helper_method :current_user, :can_edit_customers?
def current_user
#current_user ||= User.find_by_id(session[:user])
end
def can_edit_customers?
#current_user.can_edit_customers?
end
This way the method also becomes available in the view.
<% if can_edit_customers? -%>...<% end -%>
Just to be more direct. The helper_method "macro" in a controller causes a controller method to behave as if it's also a method in the application helper.
helper_method :current_user, :can_edit_customers?
Personally I think you should not use helpers.
I would take a different solution.
Let's say that we have Cucumber, a model, that should not be editable by some users. I create #editable? method as follows:
class Cucumber < ActiveRecord::Base
def editable?(current_user)
# Something happens here.
end
end
Note that if a page is accessible by everyone, current_user might be false.
Then, in the views you're able to do:
<%- if #cucumber.editable?(current_user) -%>
<%# Something happens here. -%>
<%- end -%>
And, in the controllers, use a filter.
The best thing about this approach is that it follows Fat Model, and enables you to easily cover your permissions with unit tests.
I think helpers are the way to do what you want. As for checking in views whether the user has or not the priviledge to do something, you could put a flag in your session data (e.g. session[:admin] = true and check that in your view.
It is common practice to make methods like logged_in? available in the controller and the views. For most cases, you don't need to push down authorization logic into the models.
Definetly go with with the approach user Hates described. Have a look at plugins like restful_authentication and acts_as_authenticated, to see how they did it.
There are several railscasts (http://railscasts.com/episodes?search=authentication) covering this topic. For instance, you could write a helper that takes a block and is then used like this:
<%- admin_user_ do %>
<%= link_to .. %>
<%= link_to .. %>
<%= link_to .. %>
<%- end %>