I am having trouble with my first project in ruby on rails - ruby-on-rails

Here's my index action in the books controller: http://pastebin.com/XdtGRQKV
Here's the view for the action i just mentioned: http://pastebin.com/nQFy400m
Here's the result without being logged in: http://i.imgur.com/rQoiw.jpg
Here's the result when i'm logged in with the user 'admin': http://i.imgur.com/E1CUr.jpg
So the problem is that, in the view, before line 25 the 'user' variable seems to be empty ( or not loaded), and after line 25 the variable 'user' has the expected values.
I have tried initializing a variable in the index method of the books controller but get exactly the same results.
Thanks in advance!
BTW had to make the links text because of stackoverflow limit.

This:
user = User.find_by_id(session[:user_id])
should be in controller, not in view (MVC!) like this:
#user = User.find_by_id(session[:user_id])
Then in your view, as #Voyta answered, use <%= #user.username %>. Code inside <% %> is evaluated, but not rendered, so if you want to put result in your html, you need to add =.
And all yours if user and if user.admin == 1 would look much better this way:
<% if user %>
<td><%= link_to 'Show', book %></td>
<% if user.admin == 1 %>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Delete', book, :confirm => 'Are you sure?', :method => :delete %></td>
<% end
end
%>
If you use if in single line like here:
<%
if user
if user.admin == 1
%>
<%= link_to 'New book', new_book_path %>
<%
end
end
%>
You can write the same like this:
<%= link_to 'New book', new_book_path if user && user.admin == 1 %>

You didn't output user.username. It should be <%= user.username %>, not <% user.username %>

Related

Button event trigger

I am having trouble understanding as how do you trigger specific button event when using Ruby on Rails. For example I have a mini form inside a table as so (View side of MVC):
<%= form_tag do %>
<% if student.floor_pref == '1st' %>
<td><%= select_tag 'room', options_for_select(#first_floor.map { |value| [value,value]}, #selected_room) %></td>
<% end %>
<% if student.floor_pref == '2nd' %>
<td><%= select_tag 'room', options_for_select(#second_floor.map { |value| [value,value]}, #selected_room) %></td>
<% end %>
<% if student.floor_pref == '3rd' %>
<td><%= select_tag 'room', options_for_select(#third_floor.map { |value| [value,value]}, #selected_room) %></td>
<% end %>
<td><%= submit_tag 'Update' %></td>
<% end %>
How do I tell the controller that when the Update button is clicked inside the view to for example execute this code:
a = Student.find(3)
a.room_number = '2105'
a.save
Your button should be triggering a post-back to the server. You need to define an action in your controller, a route to reach that action, and set your forms action attribute to that route. Your action can then update the models and respond with a success or error message.
u should specify a route(a controller action to which the data is to be submitted) in the form tag. refer http://apidock.com/rails/ActionView/Helpers/FormTagHelper/form_tag for more details. considering that ur action is for 'edit' action from students controller and that data is to be posted to 'update', the view can be defined as
<%= form_tag student_path(#student), :method => :put do %>
...
<$= submit_tag 'Update' %>
and in the 'update' method, u can write the code to update student with the received params. I think u need to study basic rails functioning since it will save u time in future. u can refer http://guides.rubyonrails.org/getting_started.html Also, prefer using form_for rather than form_tag.

how to stop cache from interfering with application logic

I'm trying to use Rails 4 cache digests in my app, but am finding that it interferes with a lot of application logic. For example, in this code, the links that are supposed to only be revealed where the current_user's id matches #user.id aren't working properly when I have the cache surrounding the code. Those links are visible to anyone who views the page.
<% cache #languages do %>
<% for language in #languages%>
<tr>
<td><%= language.name %> </td>
<% if current_user && current_user.id == #user.id %>
<td><%= link_to "edit", {:controller => 'lawyer_profiles', :action =>'show', :language_id =>"#{language.id}"}, {:class => "editarea #{language.id}"}%></td>
<td><%= link_to "destroy", language, :confirm => 'Are you sure?', :method => :delete %></td>
<% end %>
</tr>
<% end %>
<% end %>
In this code, the cache was interfering with the #question instance variable. For example, due to the presence of the cache, when I clicked on the link to "Add An Answer," it had my answering a different #question than the one I should have been answering, because the cache kept a memory of the instance variable on a page that I had already navigated away from.
<% cache #answers do %>
<% if #answers.empty? %>
<div class="row">
<h5>This question hasn't been answered yet: <% if can? :create, #answer %>
<% if current_user && !current_user.answers.map(&:question_id).include?(#question.id) %>
<%= link_to "Add an answer", new_question_answer_path(#question) %>
<% end %>
<% end %> </h5>
....some code not included
How can I stop the cache from interfering with logic like this?
You'll have to add the logic into the cache key.
So...
cache [#answers, current_user.answers.map...]
This will base the view on what the user's answer is...you may want to break down the rest into more fragment caches in addition to the above.

New instance of a linked model, pass 'caller' id

I'm creating a simple inventory app, there is a view that lists 'items'. It has tables with these rows:
<tr>
<td><%= item.title %></td>
<td><%= item.desc %></td>
<td><%= item.value %></td>
<td><%= item.room.name %></td>
<td><%= item.user.username %></td>
<td>
<%= link_to 'View', item %>
<%= link_to 'Edit', edit_item_path(item) %>
<%= link_to 'Delete', item, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'Add Comment', !?????! %>
</td>
/tr>
I have a linked model for 'comments' set up but don't know how to pass the 'item_id' to it when creating a new one.
The URL helpers actually accept the object to make a route for an association. Meaning, assuming you have a nested route for comments within items,
resources :items do
resources :comments
end
you can link_to the new_item_comments_path(item).
The method new_item_comments_path(item) makes a string URL based on the new_item_comments route, which you feed to link_to to make an HTML <a> tag.
To be clearer, in your view you would have:
<%= link_to 'View', item %>
<%= link_to 'Edit', edit_item_path(item) %>
<%= link_to 'Delete', item, method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to 'Add Comment', new_item_comments_path(item) #-> (instead of ???) %>
In this case, the item you are passing is the reference to your current item, which allows the URL helper to make a URL for it from the route.
The Rails guide for routing should be a useful read for you.
Now that's assuming your Comment controller assigns the right stuff at the right place. You seemed to have figured that out, but I'll explain for the sake of clarity (and future visitors)
class CommentsController < ApplicationController
# GET /item/:item_id/comments/new
def new
#comment = Comment.new
#item = Item.find(params[:item_id])
#comment.item = #item
# render
end
# POST /item/:item_id/comments
def create
#comment = Comment.new(params[:comment])
#item = Item.find(params[:item_id])
#comment.item = #item
# if #comment.save blah
end
end
All credit should go to #jonallard
The solution is all about routing it seems, you need to pass a url to the form that makes new comments (linked models).
to do this:
Add this (or similar depending on object names) to the page that is calling the creation:
<%= link_to 'Add Comment', new_item_comment_path(#item) %>
In both the new and the create method of the comments_controller there is a line starting #comment = Comment.new. Under that line add:
#item = Item.find(params[:item_id]) AND
#comment.item = #item
edit the top line of the comments template for to: <%= form_for(#comment, {:url => item_comments_path(#item)}) do |f| %>
Edit routes to somethings like:
resources :items do
...
resources :comments
end
and Read this: http://guides.rubyonrails.org/routing.html#creating-paths-and-urls-from-objects!
As mentioned before all credit goes to #jonallard, his answer and his expertise.

Link_to another view ruby on rails

I am trying to use the link_to feature to link one view to another.
The view i am calling link_to is app/views/instructors/show.html.erb and that snippet of code looks like this (namely, the second to last line of it)
<% provide(:title, #instructor.login) %>
<% courses = Course.where(:instructor_ID => #instructor.id) %>
<div class="span2">
<h1 align=center ><%= #instructor.login %></h1>
<%= link_to "Add course", new_course_path(:instructor_ID\
=> #instructor.id), :class => "btn" %>
<br>
<br>
<%= link_to "Remove course", delete_course_path(courses), :class => "btn"%>
</div>
The view I am trying to link to is is app/views/courses/show_all.html.erb and looks like this:
<% #courses.each do |course| %>
<tr>
<td><%= course.course_name %></td>
<td><%= course.instructor_ID %></td>
<td><%= link_to 'Show', course %></td>
<td><%= link_to 'Edit', edit_course_path(course) %></td>
<td><%= link_to 'Destroy', course, :method => :delete, :data => { :confirm => 'Are you sure?' } %></td>
</tr>
delete_course_path routes to app/views/courses/show_all.html.erb shown above. When I try the code above, I get the following error:
undefined method `each' for nil:NilClass
At this line:
<% #courses.each do |course| %>
Any ideas what i'm missing in my link_to?
In your show_all action, you should define a #courses instance variables. This is
<% courses = Course.where(:instructor_ID => #instructor.id) %>
not passed to show_all.html.erb.
An instance variables is a variable passed from action of controller to the view corresponding.
I suppose when you show page of instructor, your route will like this: /instructors/:id, so maybe in your show_all action of instructor controller, you need something like:
def show_all
#courses = Course.where(instructor_ID: params[:id])
render 'courses/show_all'
end
This means that #courses is nil. Did you set it in your show_all action of your controller? E.g.
def show_all
#courses = Course.all
end
Also, in your show view, you set courses to a collection of Course objects, but your "Remove course" link looks like you only want to delete one course. Why do you use the delete_course route to link to your show_all view?

rails way of doing if/else logic

I have following code which seems to work fine:
<%if group.employees.count > 0%>
<td><%= link_to group.employees.count.to_s, {:action => 'index', :controller => 'employees'}, {:id=>group.id}%></td>
<%else%>
<td><%= link_to "Add Employee", {:action => 'new', :controller => 'employees'}%></td>
<%end%>
I'm just wondering how this can be written in more rails friendly way using unless?
I would (with dylanfm's advice in the comment underneath) write it like this:
<% if group.employees.present? %>
<td><%= link_to group.employees.count.to_s, employees_path, { :id=> "group_#{group.id}" }%></td>
<% else %>
<td><%= link_to "Add Employee", new_employee_path %></td>
<% end %>
Here I've used the employees_path and new_employee_path methods which are generated by using the routing helpers in config/routes.rb. You can read more about routing by reading the Routing from the Outside In guide.
I consider it unreadable to use unless with else. Would be better to stick with what you have.
You have an if-else branch (two possible different outcomes), so using unless would require you to change it to an unless-else branch, which is hard to understand. If-else is the way to go in this situation.
If you only had a single condition, i.e.
if foo != 0
do_something(bar)
end
It could be rewritten as
do_something(bar) unless foo == 0
You can try this way:
<% if group.employees.any? %>
<td><%= link_to group.employees.count, employees_path, { :id=> dom_id(group) } %></td>
<% else %>
<td><%= link_to "Add Employee", new_employee_path %></td>
<% end %>
You can use helper and content_tag to generate html instead of ugly erb grammar.
def foo
if group.employees.count > 0
content_tag(...)
else
content_tag(...)
end
end
then in your view
<%= foo %>
Tumtu's way is the best, but I would even put the td tags outside the if statements. I would never use unless with else, and certainly is not a "rails way" to use unless.

Resources