Template error based on undefined method 'custom_helper' - ruby-on-rails-4

I have a custom helper in app/helpers/posts_helpers.rb:
module PostsHelper
def custom_helper(post)
#do something
end
end
When I call this from one of views, I get the error:
ActionView::Template::Error (undefined method 'custom_helper' for class..
But if I call the same helper from app/helpers/application_helper.rb as follows, then the view is able to detect the helper.
module ApplicationHelper
def custom_helper(post)
#do something
end
end
Even tried setting the include all helper option to true explicitly although it is true by default in config/application.rb. That didn't help either.
config.action_controller.include_all_helpers = true
Why didn't it work?

By default, methods in helpers are available only to their corresponding controllers. For example, PostsController has access to methods in the PostsHelper, but the various posts views don't. If you want to make those methods available to the views, designate them as helper_methods, like so:
module PostsHelper
helper_method :custom_helper
def custom_helper(post)
#do something
end
end
In contrast, methods defined in ApplicationHelper are available globally, i.e. in all controllers and views.
You can read the excellent answer provided here for more detail.
You can also include a module in ApplicationHelper to give its methods global accessibility:
module ApplicationHelper
include PostsHelper
...
end
That's not always a good idea though, because it can make your code difficult to understand and maintain later.

Related

Undefined method error for helper method on view in rails Engine

I am facing the following problem: in my application I use engines. Let's say I have a shop engine. Within that shop engine I have two controllers: carts_controller and products_controller and their helpers: carts_helper and products_helper.
Now on my views/shop/products/index.html.erb view, I try to call the method cart_action which is defined in helpers/shop/carts_helper.rb. However, unfortunately I get a undefined method `cart_action' for #<#<Class:0x007fb3627af090>:0x007fb3627aab08> when I do this. When I place the same method in helpers/shop/products_helper.rb I do not get this message and the method works fine.... Why can't I use the method from carts_helper, but can I use the method from products_helper? In a normal rails app I can use any helper method on any view, right?
It might have to do something with the namespacing i.e. the helper files are not in helpers but in helpers/shop however this helps prevent conflicts with helpers from other engines or apps...
module Shop
module CartsHelper
def cart_action(package_id)
#some code
end
end
end
How I call it on shop/products/index.html.erb:
<%= cart_action(package['id']) %>
Could it have to do with the fact that I inherit functionality for my applications_controller from my main app?:
class Shop::ApplicationController < ApplicationController
end
instead of
module Shop
class ApplicationController < ActionController::Base
end
end
FWIW my routes for this engine looks like:
Shop::Engine.routes.draw do
resources :products, only: [:index]
# shopping cart
resource :cart, only: [:show] do
put 'add/:package_id', to: 'carts#add', as: :add_to
put 'remove/:package_id', to: 'carts#remove', as: :remove_from
end
end
Thanks for any help in advance!
Note: I do not want to use my helper method in my main app, rather just on another view in the same engine.
In addition to the ApplicationHelper, only view-specific helpers are accessible to the view. Since this is your product-related view, only the ApplicationHelper + ProductsHelper are accessible. So the solution is to either move this method to ProductsHelper or to ApplicationHelper.
I found two ways to make the methods available to other views as well:
By creating an initializer in my /my_engine/lib/my_engine/engine.rb file as described here: https://stackoverflow.com/a/9641149/3519981
Or by including helper :all in my controllers/myengine/application_controller.rb file as described here: https://stackoverflow.com/a/1179900/3519981
Note both will make all the helpers available in the main application as well. For me that is not a problem (at the moment).
Not sure if anyone here has the same issue as me, but basically after I made a new custom helper and it wasn't accessible in a view, all I had to do was restart the rails server. I felt pretty silly after realizing that, well also relieved :-)

How to test templates with hiera data locally?

I'm trying to figure out a way to take a erb template file and use hiera data (even from a single yaml file) and just use the template to generate a file with replaced values.
Have you ever tried anything like this? My first thought is to write a ruby script, but maybe there's a simpler solution.
Thanks in advance.
Edit:
Since it might not be clear, let me explain the use case.
I want all application configuration to be templated and committed by developers and I want to provide them an automated way of filling those templates in on their local machines (laptops) without using puppet. The extra benefit is validation of templates before actually committing them.
I'm not sure why you would want to do this from hiera data directly, but it would be easy to implement with the ERB and yaml ruby libs. Something like (psudo-code):
class Erbwritter
require 'erb'
require 'yaml
attr_accessor :output_path, :yaml_path
def initialize(template, command)
#output = :output_path
#data = :yaml_path
.....
end
def render()
ERB.new(#template).result(binding)
end
def save(file)
File.open(file, "w+") do |f|
f.write(render)
end
end
def parse_yaml(#data)
File.open(#data, ...
# parse some stuff, add them to a local {}
end
end
Then, you can instance this class like:
newTemplate = Erbwritter.new(/path/to/output, /path/to/yaml)
newTemplate.save(File.join(Dir.pwd, your_file_name"))
Again, this is all basically psudo code and won't work out of the box, but it's pretty close. So have fun.
You can read more about ERB Class here.

Variable from partial to container

The goal
I need to do something like this on my layout /views/layouts/application.html.erb:
<% if free %>
<!-- do something -->
<% end %>
BUT, the free variable must be declared on the partials or yielded views, not in controllers or models – this is a visualization rule.
The problem
Currently, I'm doing the following in my /controllers/welcome.rb:
def index
#free = true
end
The point is: this isn't reponsibility of the controller and then I don't feel things good this way.
Environment
CentOS 6.5 with Rails 4.1.
this isn't reponsibility of the controller and then I don't feel
things good this way.
I understand. Although you should not be declaring variables on your view, that could get messy. Imagine if there's a bug with the variable and you would be looking into all your views that set the variable looking for the problem. IMHO it is way better to declare it as a helper in your ApplicationController, that way all the views will have your variable already defined and it will be up to you to use it or not.
class ApplicationController < ActionController::Base
helper_method :free
protected
def free
true
end
end
This way you would be able to override the 'free' method in other controllers if the value of free should be different.

Rails 4 sharing form partials and strong parameters

I have a contact form partial that I include in two different forms on my app. I was wondering if there is a better approach to strong parameters than duplicating the permitted params in each respective controller?
Users controller:
def user_params
params.require(:user).permit(:name, :email, contact_attributes: [:city, :state])
end
User Applications controller:
def user_application_params
params.require(:user_application).permit(:gender, :birthdate,
user_attributes: [contact_attributes: [:city, :state]])
end
So ideally this code would be in one place, I'm not sure how to achieve this though.
First, let me say, whitelisting your all params automatically is a hack that might be ok in some situations but could cause some substantial security risks in others and exposes you to risk as your codebase evolves (so it's never really ok). Here's a much better way to solve the problem that is about as easy and should work in just about all situations.
Instead, write a controller concern. This is just a module that goes into your app/controllers/concerns directory.
so create app/controllers/concerns/shared_contact_permitted_attributes.rb as follows:
module SharedContactPermittedAttributes
def shared_contact_permitted_attributes
contact_attributes: [:city, :state]
end
end
Then in whatever controller you want it to show up in, add include SharedContactPermittedAttributes at the top and use the method defined in the module in your strong params. For example:
def user_params
params.require(:user).permit(:name, :email, shared_contact_permitted_attributes)
end
Then you're done!
What I ended up doing was permitting all parameters and blacklisting certain ones. The blacklist params is a lot shorter than the whitelist.
params[:profile].permit!.except(user_attributes: [role_ids: []])
This allowed me to easily reuse certain form partials.

Can a django template know whether the view it is invoked from has the #login_required decorator?

Let's say that I have a system that has some pages that are public (both non-authenticated users and logged-in users can view) and others which only logged-in users can view.
I want the template to show slightly different content for each of these two classes of pages. The #login_required view decorator is always used on views which only logged-in users can view. However, my template would need to know whether this decorator is used on the view from which the template was invoked from.
Please keep in mind that I do not care whether the user is logged in or not for the public pages. What I care about is whether a page can be viewed by the general public, and the absence of a #login_required decorator will tell me that.
Can anyone throw me a hint on how the template would know whether a particular decorator is being used on the view from which the template invoked from?
Yes, it is possible, but not terribly straightforward. The complicating factor is that Django's login_required decorator actually passes through 2 levels of indirection (one dynamic function and one other decorator), to end up at django.contrib.auth.decorators._CheckLogin, which is a class with a __call__ method.
Let's say you have a non-django, garden-variety decorated function that looks like this:
def my_decorator(func):
def inner():
return func()
return inner
#my_decorator
def foo():
print foo.func_name
# results in: inner
Checking to see if the function foo has been wrapped can be as simple as checking the function object's name. You can do this inside the function. The name will actually be the name of the last wrapper function. For more complicated cases, you can use the inspect module to walk up the outer frames from the current frame if you're looking for something in particular.
In the case of Django, however, the fact that the decorator is actually an instance of the _CheckLogin class means that the function is not really a function, and therefore has no func_name property: trying the above code will raise an Exception.
Looking at the source code for django.contrib.auth.decorators._CheckLogin, however, shows that the _CheckLogin instance will have a login_url property. This is a pretty straightforward thing to test for:
#login_required
def my_view(request):
is_private = hasattr(my_view, 'login_url')
Because _CheckLogin is also used to implement the other auth decorators, this approach will also work for permission_required, etc. I've never actually had a need to use this, however, so I really can't comment on what you should look for if you have multiple decorators around a single view... an exercise left to the reader, I guess (inspect the frame stack?).
As unrequested editorial advice, however, I would say checking the function itself to see if it was wrapped like this strikes me as a bit fiddly. You can probably imagine all sorts of unpredictable behaviour waiting to happen when a new developer comes to the project as slaps on some other decorator. In fact, you're also exposed to changes in the django framework itself... a security risk waiting to happen.
I would recommend Van Gale's approach for that reason as something that is explicit, and therefore a much more robust implementation.
I would pass an extra context variable into the template.
So, the view that has #login_required would pass a variable like private: True and the other views would pass private: False
Why does your template need to know this? If the #login_required decorator is used, the view itself prevents people who aren't logged in from ever reaching the page and therefore never seeing the template to begin with.
Templates are hierarchical so why not have a #login_required version and a "no #login_required" version, both of which inherit from the same parent?
This would keep the templates a lot cleaner and easier to maintain.