I have the following evaluation which works great:
In my listings/detail.html.erb view
<% if user_signed_in? && current_user.id == #listing.user_id %>
I use this several times in my views so I wanted to make this a helper method.
What code can I place in my listings_helper file so I can call something like this in my view instead:
<% if isListingOwner? %>
Any help would be appreciated. Thanks.
When I need to do something like that, I use Cancan. Then you can write stuff like:
<% if can?(:update, #listing) %>
Listing
<% end %>
In my view much cleaner.
Put it to /app/helpers folder.
Railscast has intro tutorial on this topic
http://railscasts.com/episodes/64-custom-helper-modules
All good suggestions, but the code you need to add to your listings_helper.rb is
def isListingOwner?
user_signed_in? && current_user.id == #listing.user_id
end
Personally, I'd rather put that check in the model:
class Listing
def owned_by?(user)
user.id == self.user_id
end
end
Then in your view, you would write:
<% if #listing.owned_by(current_user) %>
You might want to look into a role based authorization plugin if you're doing a lot of this type of thing.
If you have a belongs_to :user in your Listing model, why don't you simply do
if current_user == #listing.user
I'm assuming you're using Devise or something that returns nil from current_user when the user is not signed in.
Related
I am using a namespaced Presenter object to help refactor some view presentation logic for my model attributes.
For one object being sent from the controller I would do
#user = Users::UserPresenter.new(#user)
and that works fine. For a query of users, I created a .present() method that maps and applies the UserPresenter.new to each user, so I do
#users = Users::UserPresenter.present(users)
and that works fine. But what about when I am passing an object that then iterates through a relationship in the view. A simple example would be
<% appointment.users.each do |user| %>
<% user = Users::UserPresenter.new(user) %>
<li> <%= user.age%></li>
<% end %>
A more complex example would be
<% appointment.appointment_host.family.users.each do |user| %>
<% user = Users::UserPresenter.new(user) %>
<li> <%= user.age%></li>
<% end %>
user_presenter.rb
module Users
class UserPresenter < SimpleDelegator
# methods
end
end
I don't like having to set the Presenter object in the view. What is a better way to handle this? Ideally using similar patterns as I have so far.
Perhaps you could create a hierarchy of presenters similar to model associations, and then pass only the root presenters to the view. Something like this:
class AppointmentPresenter
def initialize(appointment)
#appointment = appointment
end
def users
Users::UserPresenter.present(#appointment.users)
end
def host_family_users
Users::UserPresenter.present(#appointment.appointment_host.family.users)
end
# or perhaps even indeed create a presenter for each collection:
def appointment_host
AppointmentHostPresenter.new(#appointment.appointment_host)
# this presenter would have the `family` method returning a FamilyPresenter, etc.
end
end
I.e. some kind of "decorators" for the model associations, returning presenters instead of model objects.
I have the following code in a Rails partial being used in some mailers but am not happy with my solution and have the feeling this is far from optimal.
I have an email which
From my mailer:
def the_email_i_am_sending(user, inquiry, params = {})
get_variables(inquiry) #This also provides access to my `#user` object
#contact_name = [params[:guest_last_name].to_s, " ", params[:guest_first_name].to_s].join
I always have #user but on occasion a specific partner will call our API with additional params of [:guest_last_name] and [:guest_first_name] as defined above. This allows me to define #contact_name as a separate instance variable.
When this is .present? i.e. not nil, I want to render #contact_name in a field on the email rather than the #user.login that would pull from our DB.
My mailer view then uses the following code to decide which partial it will render.
<% if #contact_name.present? %>
<%= render 'meet_your_guest_v3', tujia_guest: #contact_name %>
<% else %>
<%= render 'meet_your_guest_v3' %>
<% end %>
My solution is then to utilise this code in the partial being rendered in the mailer. It seems a little verbose but I am unsure about the correct usage of local_assigns.has_key?
<% if local_assigns.has_key?(:partner_guest) %>
<%= partner_guest %> <p>(via our partner</p>
<% else %>
<%= #user.login %>
<% end %>
Is there a better way?
You should definitely follow the advice from #Jon regarding dealing with params in your controller/mailer. Additionally you should just pass #contact_name every time to the underlying partial, regardless if it is present or not, then check only where you want to render it, if it is present. This way you would skip one conditional:
#email_view.html.erb
render 'meet_your_guest_v3', parnter_guest: #contact_name
_contact_name.html.erb
<% partner_guest.present? %>
...
A further step could be using a special decorator object, which would deal with the presentation logick. It would check wether contact_name was provided from outside or from the model and render the desired html tag for the contact_name (or it could just return it as string). See following pseudocode using the draper gem:
class MyController < ApplicationController
def send_mail
#user = User.find(...).decorate(
contact_name: [params[:guest_last_name].to_s, " ", params[:guest_first_name].to_s].join
)
MyMailer.the_email_i_am_sending(#user)
end
end
class MyMailer < ApplicationMailer
def the_email_i_am_sending(user)
#user = user
mail(to: ..., subject: ...)
end
end
class UserDecorator < Draper::Decorator
def contact_name_tag
if (contact_name.present?)
h.content_tag(:div, contact_name)
else
h.content_tag(:div, user_name)
end
end
end
#email_view.html.erb
<%= #user.contact_name_tag %>
However if the presentation logic isn't very complicated, going with a couple conditionals and perhaps extracting them into basic rails helpers is fine and using a presenter may be an overkill
I am using Devise for authentication in my rails app and I have a _header partial in my layout folder for navbar. I wanna put there a link for Create Profile (user model is created w/ devise, user has_one profile and profile belongs_to user) but only if the user profile doesn't exist yet.
I would like to create a method for this and put the if statement into the view but I can't figure it out where to create the method and how it would look like.
The basic devise method works fine when it comes to checking if user is signed in. I want a similar method that can check if user profile exists.
layout/_header.html.erb
<% if user_signed_in? %>
<% if user.profile(current_user) %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
So my questions:
Where to put the method (helper/controller/model/appcontroller/etc.)?
How the method would look like?
You can define it in your Helper files (app/helpers/). You can use the application_helper but for a better consistency we will name this file users_helper:
# app/helpers/users_helper.rb
module UsersHelper
def user_has_profile?(user = current_user)
return false unless user.present?
Profile.where(user_id: user.try(:id) || user).exists?
end
end
and use it like this:
# any view
<% if user_signed_in? && !user_has_profile? %>
I would put this in the helpers directory(app/helpers/application_helper.rb) as a method called has_profile?
the method would look like
def has_profile?
current_user.profile.present?
end
then in your view:
<% if user_signed_in? && has_profile? %>
<li><%= link_to "Create Profile", new_user_profile_path(current_user) %></li>
I have a review model that I would like to lock out the edit and destroy button after 30 minutes and if only the correct user. Right now I just have an if statement around the button but you can still get to it by putting in the full URL. Where/ how would I go about doing this? I am new to Ruby on Rails and any help is useful. Thanks in advance!
Edit: All I have is below in the index but the problem is that I can still get to it through the URL and I don't know how to make those inaccessible after that.
<% if signed_in? %>
<% if current_user.id = review.user_id %>
<% if !review.has_time_passed? %>
<td><%= link_to 'Edit', edit_property_review_path(review.property, review) %></td>
<% if !review.comments.any? %>
<td><%= link_to 'Destroy', [review.property, review], :confirm => 'Are you sure?', :method => :delete %></td>
<% end %>
<% end %>
<% end %>
<% end %>
My has_time_passed method:
def has_time_passed?
created_at < 30.minutes.ago
end
There's at least 2 pieces to what I think you're describing:
You need to ensure the view template hides any edit and destroy links after 30 minutes.
You need to add logic to the relevant controller actions (edit and destroy) to ensure they'll refuse to make any changes after 30 minutes.
As far as the view logic goes, it sounds like you're pretty close and this shouldn't be too difficult. One if statement phrasing that comes to mind (similar to what you pasted above, but a tiny bit simpler):
if review.created_at <= 30.minutes.ago
Then, in the controller, you'll also want to ensure the action only makes changes within the time limit. So for example you might use the same if statement:
def edit
if review.created_at <= 30.minutes.ago
redirect_to some_other_path, alert: "Sorry bro, this review is too old to be edited."
else
# do stuff
end
end
That's just a very rough-draft example. Once you have everything working, then extract the logic into a method on the model (to reduce redundancy) and so forth.
Good luck!
My recommendation would be to introduce an authorization framework like Pundit or Cancan.
That way you separate the logic that interacts with the model from the controller, for which it's a good idea to keep it as minimalistic as possible.
Both Pundit and CanCan have great tutorials that show how to achieve similar scenarios to what you are trying to achieve.
In Pundit, for example, your policy would like somewhat like this:
class MyModelPolicy
attr_reader :user, :my_model
def initialize(user, model)
#user = user
#my_model = model
end
def destroy?
user == my_model.user && my_model.created_at < 30.minutes.ago
end
end
I think you want something at the controller level.
# app/controllers/reviews_controller.rb
class ReviewsController
before_action :validate_change, only: [:edit, :update, :destroy]
def edit
# edit stuff
end
def destroy
# destroy stuff
end
private
def validate_change
if #review.created_at < 30.minutes.ago
redirect_to request.env['HTTP_REFERER']
end
end
end
I have a page with a configuration part where some users can edit element and variable of the site.
I want to cancan controle the access to the page but as it's not a model, and I'm not sure how to do.
My controller
class ConfigurationController < ApplicationController
def index
end
end
My menu
<% if can?(:read, Configuration) # not sure of that %>
<li><%= link_to t('texts.configuration.title'), configuration_index_path %></li>
<% end %>
I have a error with this code :
uninitialized constant ActionView::CompiledTemplates::Configuration
I'm actualy not sure of what mean this error.
What will be the correct way to do that?
Thanks.
This is possible follow this post on Github it describe your problem and how to solve it:
https://github.com/ryanb/cancan/issues/22
and also this:
CanCan and controllers without models