Draper Decorators and using methods on Application controller - ruby-on-rails

I have some code in my view that tests if a user is logged in with a method that is on the application controller:
def logged_in?
!!current_user
end
I use this method on a view to display certain information only if the user is logged in.
<%= **logged_in?** ? link_to('+', upvote_post_path(post), method: 'post', remote: true) : '+' %>
<span id = "post-<%= post.slug %>-votes"> <%= post.total_votes %></span>
Now if I want to move this logic to a decorator it won't work because the decorator cannot access this method on the application controller. I need this to be on there. So my question is: What is the best way to handle this situation? The logic can be access from the application helper. Is there a way that my decorator object access this method without repeating the method twice?

you can access helper h.some_methods
def link_to_edit_event
h.link_to h.t('buttons.edit'), h.edit_event_path(id)
end
from Draper
Normal Rails helpers are still useful for lots of tasks. Both Rails'
provided helpers and those defined in your app can be accessed within
a decorator via the h method:
class ArticleDecorator < Draper::Decorator
def emphatic
h.content_tag(:strong, "Awesome")
end
end

Related

How do I initialize or show a nested form in rails?

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.

Are application helper methods available to all views?

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.

Rails with Cells and Helpers

I'm having trouble accessing the cookies object in my cell views via helper. My code looks like this:
#cell
helper SessionsHelper
#cell view
signed_in?
#sessions helper
signed_in?
cookies.sth
end
I'm getting the error: undefined local variable or methodcookies'`.
How do I make cookies visible there?
Alternatively, I'd like to pass Helper as an object collaborator to my cell, because this helper contains a lot of useful methods. Is doing SessionHelper.new the correct way to do that?
<%= render_cell :my_cell, :display, session_helper: SessionsHelper.new %>
I now see that SessionsHelper is in fact a module, so I cannot invoke the new() method. What should I do with undefined cookies?
I always define signed_in? in ApplicationController. (There cookies is available) And then do:
helper_method :signed_in?
to make it available as a helper method.
As for your second question: session_helper: SessionsHelper.new is not necessary. All methods from all helpers are available all views.

Rails: method defined in application_helper.rb not recognized by categories_controller.rb

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.

What is the best way to avoid code duplication between views and controllers in Ruby on Rails?

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 %>

Resources