Rails 3.1 - Using Devise/Cancan with Prawn - ruby-on-rails

In my html.erb views I can normally use code like:
if current_user.role?(:label)
"do something"
end
I'm trying to do the same in a Prawn based PDF but am getting a "undefined local variable or method `current_user'" error.
I understand I need to explicitly include a helper in my PDF class, something like:
class SalesnotePdf < Prawn::Document
include CanCan::Ability
"do pdf layout here"
end
But this doesn't appear to work? Any ideas on the correct include or other approach to this?
Thanks in advance!
EDIT:
I think current_user is actually a Devise method I need to access.

Well, current_user really is a Devise method and is available in views, helpers and controllers, but not models. A better approach would be to check the user's role in a controller and then call a specific Prawn document.

Unless each user needs a significantly different report, you could pass view_context as one of your parameters.
Controller
...
format.pdf do
...
pdf = MyPdf.new #records, reportDate, view_context
...
end
Then in MyPdf you pick that up...
def initialize(records, reportDate, view)
super()
#nonPagedTransactions = transactions
#view = view
....
So now it is available to all your methods in MyPdf (as well as other view-context helpers), if you say...
#view.current_user

Related

Rails create a helper to add text

I have a fee column in my model and it is an integer type, so I try to create a tiny helper to add a dollar sign neatly in front. Which means, instead of writing:
span = "$#{#object.fee}"
I can write something like
span = #object.fee.dollar
So I created the tiny helper.
module ApplicationHelper
def self.dollar
"$#{self.try(:to_s)}"
end
end
I am not sure where to put it, so basically it's now showing
undefined method `dollar' for 180:Fixnum
number_to_currency()
Rails 4.2 has this ActionView::Helper
number_to_currency(1234567890.506)
Helper
If you want to implement this as a helper, this works
module ApplicationHelper
def dollar(amount)
amount = number_to_currency(amount)
end
end
Invoke
<%= dollar(your_var_here) %>
Rails spec for number_to_currency()
http://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_currency
Note: Other versions of Rails may have this function, you'd have to check your version.
module ApplicationHelper
def dollar(amount)
"$#{amount}"
end
end
and then:
span = dollar #object.fee
I think it's because you're in a helper, so you can't refer to self.
You can do it in your Model, or in the helper do :
def print_dollar(your_value)
Or, you can also use : number_to_currency(dollar, :unit => "$"), which will render it the way you want.
Hope it help
Your helpers are included in the view context, so you need two changes:
def dollar - because it's included in the renderer, you don't need self
Call it as dollar(#object.fee) - it's not included on the object, but in your view. If you want to call it as #object.dollar, declare the method in whatever class #object is.
Additionally, the number_to_currency helper already exists and is quite robust. Perhaps you want to use that.

Using Decorator pattern in Rails but can't access view helper

I'm trying to implement Decorators using the learnings from "Rails 4 Patterns" Code School course, but I'm running into trouble as I need a view helper in the Decorator class.
I want my view to have:
<%= #model_decorator.previous %>
Then in the decorator:
def previous
if object.prev_item.nil?
"Previous"
else
link_to("Previous", object)
end
end
The course suggests you make a call to the decorator within your view helper in the view file itself, but that's no good if the logic could output one result with a helper and one without. (i.e. need the output to be a link or not).
I've tried using helpers.link_to but it errors out as not providing the correct information for the url_for option. I've confirmed link_to("Previous", object) works within the view itself.
For Rails 4
include ActionView::Helpers::UrlHelper
link_to("Previous", Rails.application.routes.url_helpers.send("#{object.class.name.underscore}s_path".to_sym, object))
As for me it`s better to make a decorator for it:
class LinkDecorator
include ActionView::Helpers::UrlHelper
def initialize(label, object)
#label = label
#object = object
end
def show
link_to(label, url_helpers.send("#{object.class.name.underscore}s_path".to_sym, object))
end
def index
link_to(label, url_helpers.send("#{object.class.name}s_path".to_sym))
end
...
private
attr_reader :label, :object
def url_helpers
Rails.application.routes.url_helpers
end
end
Example usage:
LinkDecorator.new(object.name, object).show
If I understand your problem correctly, you essentially want links in a plain old ruby object.
My solution would be this:
include ActionView::Helpers::UrlHelper
link_to("Previous", Rails.application.routes.url_helpers.objects_path(object))
# assuming the object is always of one class
If the object is of a different class, than it would be possible to use the .send method to send the correct message to app ie.:
include ActionView::Helpers::UrlHelper
link_to("Previous", Rails.application.routes.url_helpers.send("#{object.class}s_path".downcase.to_sym, object))
# I'd create a function out of that line to make it a bit neater
It sounds like the error thrown by url_for comes from missing the routes and there's a few ways to include those. My solution kinda avoids that problem by using Rails.application.routes.url_helpers. Hope this helps!

Trying to import my helpers into a custom lib/class and suddenly I need a config?

Real simple.
class Template
def stuff_i_want
stylesheet_link_tag('my_stylesheet')
end
class << self
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
end
end
And this returns..
undefined local variable or method `config' for Template:Class
from /Users/elephanttrip/.rvm/gems/ruby-1.9.3-p385#shasta/gems/actionpack-3.1.12/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb:137:in `stylesheet_link_tag'
From the stylesheet_tag_helpers.rb in Railtie :
def stylesheet_link_tag(*sources)
#stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
#stylesheet_include.include_tag(*sources)
end
Config isn't instantiated in that file anywhere, so I'm assuming its' required from somewhere else.. I have no idea where, or how.
Anyone know how to inject/pass a config into my helper? I've never needed to do this before.
It looks like you're actually including your helpers into Object - and then defining your Template class. I've no idea why it's asking for config, but try putting the includes inside your class definition and see if the problem goes away.
You probably shouldn't be randomly including helpers into things over than your views though - that's not what they're for.
Why not use view_context instead.
So instead of including the helper modules you can do this:
class Template
def stuff_i_want
view_context.stylesheet_link_tag('my_stylesheet')
end
end
This should work fine.
And if you want to include you helpers then use the below code:
class Template
include ActionView::Helpers::TagHelper
include ActionView::Helpers::AssetTagHelper
def stuff_i_want
stylesheet_link_tag('my_stylesheet')
end
end
Ideally you should not include helpers in ur controllers as they are not intended for that.
Hope that helps

Extending model to include "helper" methods

I have a kind of social network website.
I have a logic to create the path for the user, and select an avatar for each user described in UsersHelper methods user_path(user) and user_avatar(user).
Instead I want to have methods like user.path and user.avatar, but I don't want to have that code inside the model file.
I tried extending the User class inside the helper:
module UsersHelper
class User
def avatar
...
end
end
end
That doesn't work - the method I added aren't recognized (I'm guessing Rails dynamically generates the ActiveRecord class on demand so my methods don't apply?)
I'd really appreciate ideas
First, there's a reason helpers are separated from models. According to the MVC pattern your model shouldn't know anything about your helpers, and not vice versa either (the latter is usually ignored in Rails, though).
But, if you want to do this, you need to do class ::User in model UsersHelper, or the following:
module UsersHelper
end
class User
end
The reason for this is that Ruby namespaces classes. So you defined UsersHelper::User, while your model is called User. Calling the class ::User or defining it outside the module forces it into the root namespace.
However, as I said, this is not the "correct" way to do it. A better way would be how you're already doing it, or maybe using a decorator pattern.
Draper is an awesome little gem that does an excellent job of achieving the functionality you're looking for (adding view / presentation specific code while still making it "feel" like the model you're working with). We've removed almost all of our model-specific helpers after starting to use draper.
Basically, it works by defining decorators, which work like a wrapper around your model. A decorator looks and feels like the model it's decorating, but can have additional functionality defined on top of it. In addition, you can restrict access to certain fields and lock stuff down if you like.
For your example, adding a decorator would be as simple as:
(in app/decorators/user_decorator.rb)
class UserDecorator < ApplicationDecorator
decorates :user
def avatar
# your implementation
end
(in your controller)
def index
respond_with UserDecorator.decorate(User.all)
end
(in your views)
<div class='avatar'><%= user.avatar %></div>
<div class='name'><%= user.name %></div>
Helpers are intended to use with the views, not with the models.
If you wish to have something like user.avatar, you have to add it to your model.
If you want to stick it in the helpers, then in the UsersHelper add a user_avatar method.

Is there a way to use pluralize() inside a model rather than a view?

It seems pluralize only works within a view -- is there some way that my models can use pluralize too?
Rather than extend things, I just it like this:
ActionController::Base.helpers.pluralize(count, 'mystring')
Hope this helps someone else!
Add this to your model:
include ActionView::Helpers::TextHelper
My favorite way is to create a TextHelper in my app that provides these as class methods for use in my model:
app/helpers/text_helper.rb
module TextHelper
extend ActionView::Helpers::TextHelper
end
app/models/any_model.rb
def validate_something
...
errors.add(:base, "#{TextHelper.pluralize(count, 'things')} are missing")
end
Including ActionView::Helpers::TextHelper in your models works, but you also litter up your model with lots of helper methods that don't need to be there.
It's also not nearly as clear where the pluralize method came from in your model. This method makes it explicit - TextHelper.pluralize.
Finally, you won't have to add an include to every model that wants to pluralize something; you can just call it on TextHelper directly.
YOu can add a method like this in your model
def self.pluralize(word)
ActiveSupport::Inflector.pluralize(word)
end
and call it in this way
City.pluralize("ruby")
=> "rubies"
This worked for me in rails 5.1 (see 2nd method, first method is calling it.)
# gets a count of the users certifications, if they have any.
def certifications_count
#certifications_count = self.certifications.count
unless #certifications_count == 0
return pluralize_it(#certifications_count, "certification")
end
end
# custom helper method to pluralize.
def pluralize_it(count, string)
return ActionController::Base.helpers.pluralize(count, string)
end

Resources