Rails Check if User Id is in Array - ruby-on-rails

I'm trying to build a condition based on wether or not a "user" is a "member". Basically I need a way of checking if the current_user.id matches any of the user_id of any members. The non-working code I have right now is:
<% if current_user = #page.members %>
you can view this content.
<% end %>
I'm looking for something along the lines of: "If current_user.id exists in the "user_id" of any members."

Something like this, based on the field names in your question:
<% if #page.members.map(&:user_id).include? current_user.id %>
You can view this content
<% end %>

Assuming your #page.members variable contains an array, you can use the include? method:
<% if #page.members.include? current_user %>
you can view this content.
<% end %>
If you're using an array of ids, you will of course need to change the test slightly to look for the current user's id:
<% if #page.members.include? current_user.id %>
you can view this content.
<% end %>

#member_ids = #page.members.map{|m| m.id()}
then check for the condition as below
#memeber_ids.include? current_user.id()

Has said before include? should do the thing.
I'm just answering to tell you about a gem called CanCan, that gives you easy access for authorization "helpers".
Why you should use CanCan instead of doing what you are actually doing?
Don't reinventing the weel most of the times it's a goob practice.
You are placing business logic on the view (bad practice).
CanCan most likely has been developed thinking on security, and all the best practices in mind.
You save some developing hours.
Sorry if I repeated myself.

Related

Move logic like this to the controller or model rather than the view?

I have this logic currently in my view
<% tools_count = #job.tools.count - 1 %>
<% count = 0 %>
<% #job.tools.each do |u|%>
<%= u.name %>
<% if count != tools_count %>
<% count += 1 %>
<%= "," %>
<%end%>
<% end %>
Which just loops through some users relations and puts in a , unless it is the end of the list.
My question: This kind of logic looks really messy and clogs up my views I know there must be a better way of doing this by moving it into the controller or maybe model, does anyone know the correct way to do this kind of logic?
You can add a method like this to your Job model:
def tool_names
tools.map(&:name).join(',')
end
And use it in your view like this:
<%= #job.tool_names %>
There are couple of ways to avoid putting this kind of logic in the view layer:
Create an instance method in the model class (as spickermann suggested)
This will work for simple logic and simple projects. However, when you will want to use some helpers from ActionView::Helpers such as jobs_path or number_to_currency, a model is not a good place for it.
Create a helper method in helper modules eq. JobHelpers
Generally you can put any helper methods related to view layer in helpers. For example to share common methods for building a view components.
Use the decorator/presenter pattern and put there the view logic so model won't be polluted. Here is some more explanation about the pattern and sample implementation using draper gem: http://johnotander.com/rails/2014/03/07/decorators-on-rails/
You can do it in a single line like
<%= #job.tools.map(&:name).join(',') %>

Manage user permission with an instance variable or in a helper

I'm looking for the cleanest way to handle a user permission. It would be used to define if a menu option can be displayed (menu is present in all views) and the access to a page.
So I was wondering which is the cleanest way to do it.
Set an instance variable in each action from the controller validating if the user had access
Add a method in the application helper validating each time it is call if the current user have access
You can define method in your ApplicationController who will check current user permissions. And you can use that method in before_action callback for those actions you need it.
I would recommend to look at cancancan gem (it's community driven support of cancan gem)
Using it it's easy to authorize actions and check abilities to decide show menu item or not.
You can also check out RailsCast about that subject to get understanding of whole idea.
Are you trying to implement an administrator or something similar? I think the cleanest way would be to just make a new column in the users table, which is initialized to false for most, but to true if the user is an admin (or something else). Then you can just make two partials to handle the two cases.
In that case, in your menu view (in your layout or whatnot) you would have this code or something similar:
<% if current_user.admin? %>
<%= render 'admin_page' %>
<% else %>
<%= render 'user_page' %>
<% end %>
Where I assume you define #current_user in your controller, or if you are using Devise, this is handled automatically.
Edit: Yes I endorse the earlier answer, CanCan is a good gem to handle these things also, you should consider using it. In such a case your code would look something like:
<% if can? :update, #user %>
# Edit something
<%= link_to edit_profile_path(#user), class: 'user' do %>
Edit your profile
<% end %>
<% end %>

Rails output polymorphic associations

I want to implement a search functionality in my Rails app by using the pg_search gem. I've set up everything like it says in the documentation. Then I've set up a search controller with a show action:
def show
#pg_search_documents = PgSearch.multisearch(search_params)
end
The search itself works but I have a really annoying problem in my view. Whatever I do, it always outputs an array of PgSearch::Document objects. Even when I only write this in my view:
<%= #pg_search_documents.each do |document| %>
<% end %>
I get this (I've shortened it):
[#<PgSearch::Document id: 2, content: "…", searchable_id: 28, searchable_type: "Vessel">, #<PgSearch::Document id: 3, content: "…", searchable_id: 27, searchable_type: "Vessel">]
I know that pg_search sets up a polymorphic association which I've never dealt with before — could that be the problem?
Thanks in advance
<%= #pg_search_documents.each do |document| %>
<% end %>
This is a classic error, one I remember being puzzled over when I first started learning Rails. The mistake is using <%= %> with each. The return value of each is the array that you're iterating over (in this case, #pg_search_documents), and by using <%=, you're telling Rails to create a string from that array and insert it into your view. That generally isn't what you want: you want the view to be generated by the code inside the block you're passing to each.
Use <% #pg_search_documents.each do |document| %> instead (omitting the =) and you'll avoid the dump of the array's content.
You may also need to use .searchable as #blelump suggests, but I wanted to answer the other half of your question, as it's a common pitfall.
To get back to the original source model, searchable call is needed on these search result records, e.g:
<% #pg_search_documents.each do |document| %>
<%= document.searchable %>
<% end %>
You can also switch back to the source model within your controller, e.g:
#pg_search_documents = PgSearch.multisearch(search_params).collect(&:searchable)
Then, the #pg_search_documents will contain Vessel elements.

How to Handle Rails/Mongoid with no doc

When calling all posts for a user Posts.find(creator: current_user:_id), and the user hasn't made any...rails spits a "NoMethodError" for it...
What I want to do is have a pretty output for the user of "Why, no. You haven't posted anything, you lazy slob." instead of this scary error.
What's the best way to handle things like this?
You need to use where instead of find. By design, find method expect to actually find an existing thing you're looking for. Consult docs about querying here.
Also, you can try and use try method. Basically it's equal to the following:
object.try(:something_scary)
# is equal to
object && object.something_scary
This is how I handle nil entities. If you want to show some kind of message to user (about being slobby) you make a check inside of your template and render different partials. Example:
<% if #posts.present? %>
<%= render 'posts' %>
<% else %>
<%= render 'no_posts' %>
<% end %>
Then you can put your message inside of that no_posts partial.

Rails, pulling complicated logic out of views

I have a view that is getting complicated, and I'm wondering I should be doing this different? Picture (or code) is worth a 1000 words, so heres the view...
<% #orientation_by_date[date].each do |orientation| %>
<% if current_user %>
<% if orientation.active? %>
<li><%= link_to orientation.class_time, new_orientation_registration_path(orientation) %>
(<%= orientation.current_number_seats %>/<%= orientation.seats %>)</li>
<% else %>
<li><%= orientation.class_time %>(Class full)</li>
<% end %>
<%= link_to "VIEW", orientation_registrations_path(orientation) %></li>
<% else %>
<% if orientation.active? %>
<li><%= link_to orientation.class_time, new_orientation_registration_path(orientation) %>
(<%= orientation.current_number_seats %>/<%= orientation.seats %>)</li>
<% elsif orientation.class_date.before Date.today %>
<li><%= orientation.class_time %>(Class Closed)</li>
<% end %>
<% else %>
<li><%= orientation.class_time %>(Class full)</li>
<% end %>
<% end %>
<% end %>
What you are looking at is a the front end calendar view of a scheduling application. Based on differnt states, you see different information in each day on the calendar, ie, the number of seats remaining, vs. 'Class Full' vs. something else for Admins. Should I be pulling this logic into my model or controller somehow?
There are lots of ways to skin the cat. Which is 'right' is as much about personal preferences as anything else. That said, here are a few ideas that you might want to consider.
Use partials for each type of user
This may or may not be your driving concern but the outermost layer of decision making is based on user type so it may make sense to build a partial for each type of user. In this case you might have 'active_user_orientation_view' and 'guest_orientation_view'. Doing that reduces (this section) of your view down to a single if-then-else statement with pretty clear indication of your intent -- registered users see one thing and guests see something else.
Wrap-up repeating code into helper methods
Two of the list items are generated using the exact same code. Make it DRY! As an example, I'd probably drop down into the OrientationsHelper (app/helpers/orientations_helper.rb) and add a #orientation_full_item helper like this
def orientation_full_item(orientation)
content_tag(:li) do
"#{orientation.class_time} (Class full)"
end
end
With that helper in place, the two lines rendering the "Class full" message could be reduced to <%= orientation_full_item(orientation) %>. You could do the same for the list item that provides a link to the registration form. For consistency, you might do it for all of the list items. That would give you a view that very clearly declares its intentions.
Consider using a Presenter
Rather than litter your model (business logic) with view-oriented convenience methods, a better choice would be to create a new class that accepts an instance of the class and provides the same convenience methods. This is what the Presenter pattern is all about. The advantage of it is that you very clearly organize your code along the lines of it's intention -- biz logic stays together and stays untangled from view logic. In this case you might provide an ActiveUserOrientationPresenter and a GuestOrientationPresenter class, each of which provides a #list_item convenience method capable of rendering out the list item with its appropriate contents.
The PragProg guys have a title written by Bruce Williams with some great suggestions on how to build robust view code that is probably worth the money and time invested. One of the available code snippets deals specifically with presenters. You can read it http://media.pragprog.com/titles/warv/present.pdf.
Write unit tests that nail down the contents of all those <li> items with XPath.
Grab Nokogiri, and use Nokogiri::HTML::Builder to write all that in Ruby:
builder = Nokogiri::HTML::Builder.new do |doc|
doc.ul {
doc.li('data 1')
doc.li('data 2') if oodles_of_poodles?
doc.li('data 3')
}
end
puts builder.to_html
Now that it's all in one language, you can refactor it freely without constantly tripping over the escape tokens needed to mix two languages together.

Resources