Rails 3 accessing has_many through join model in another controller - ruby-on-rails

I'm doing an online judge application, so I have a User model, a Problem model and a Solution model to make the many to many relation. In that Solution model I have an extra column called "state" where I plan to store the state of a problem for a certain user: solved, wrong anwser, not solved.
I'm trying to modify the index action in my problems controller to render the state of the problem in the problem list (so a user can see if he has solved a problem or not, like I said before). Nevertheless I'm having an "uninitialized constant Admin::ProblemsController::Solution" error when I access the view.
I'm really new to RoR and my experience so far has been really harsh, so I'll appreciate any leads. Here is the code in the controller and the view:
problems_controller.rb
def index
#problems = Problem.all
if current_user
#solutions = Solution.includes(:problem).where(:user_id => current_user.id)
end
respond_to do |format|
format.html # index.html.erb
format.json { render json: #problems }
end
end
views/problems/index.html.erb
<% #problems.each do |problem| %>
<tr>
<td><%= problem.name %></td>
<td><%= problem.code %></td>
<td><%= problem.description %></td>
<% if current_user %>
<%= for solution in #solutions do %>
<% if solution %>
<td><%= solution.state%></td>
<% else %>
<td>Not Solved</td>
<% end %>
<% end %>
<% end %>
<td><%= link_to 'Show', problem %></td>
<% if current_user && current_user.is_admin? %>
<td><%= link_to 'Edit', edit_problem_path(problem) %></td>
<td><%= link_to 'Delete', problem, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% end %>
</tr>
<% end %>
I'm not sure if that's the best way I should be accessing the Solutions table or if I should be doing that in another controller (in the users controllers? in a solutions controller file perhaps?).
I want to be clear of how to use that "Solutions" join table. I had a has_and_belongs_to_many before and changed it because of the extra column. I've read a lot about many to many relationships, but I can't understand it for this case =(

Just need to use:
problem.solution.state
Unless a problem may have many solutions, then it would need to be something like:
problem.solutions.first.state
However this will just give the state of the first, so I'd define a method in Problem which calculates a status (eg. If any of the solutions solve it then problem is solved)
For 1 problem, many solutions for a given user.
In Solution.rb
scope :for_user, lambda {|user_id| :conditions => {:user_id => user_id}}
Then we can call:
problem.solutions.for_user(current_user.id).first.state
It might look a bit long but it's highly flexible.

Related

Update Attributes not well, updating attributes. id => "id"?

Good Evening, I've been trying for figure out why when I raise inspect.params I get id => "id"
Please see the code I have for updating the :approved to true from an admin form which doesn't update the attributes. I guess it's something I'm just overlooking
Form index.html.erb
<% #snippets.each do |snippet| %>
<tr>
<td><%= link_to snippet.content %></td>
<td><%= snippet.created_at.to_date %></td>
<td><%= render snippet %></td>
<td>Status</td>
<td><%= button_to 'Approve', active_snippet_path(snippet.id) %></td>
</tr>
Controller (Snippets#approve)
def approve
##snippet = #book.snippet.find(params[:id])
if #snippet.update_attribute(:approved, true)
redirect_to users_path
else
render root_path
end
end
The error is being cause by this bit apparently but not sure why:
def find_book
raise params.inspect
#book = Book.find(params[:id])
#snippet = #book.snippets
return #book
end
end
This is the inspect I get.
{"authenticity_token"=>"D70njMSz3iYbVcCCkFIlolPBKeZUsVtFL5pabRT1CMo=", "controller"=>"snippets", "action"=>"approve", "id"=>"id"}
Please let me know if you need anything else. I would like to understand clearly why this put isn't working for a nested model.
The problem is you have what seems an array of Snippets (#book.snippets) you set to #snippet, and are attempting to use the update_attribute method, which only works on one instance. If you want to update an array, you could use something to the effect of:
Snippet.where(book_id: params[:id]).update_all(approved: true)

CRUD Ownership of Items/Microposts in Rails App

I have a simple rails app where users can create 'items', but on the master index page that lists all the items, there are 'Show, Edit, and Delete' links next to each 'item'. I understand that this is due to the fact that I used scaffolding to accomplish the items, but I'd like to make sure that people can only edit the ones that they created. This logic is a little above my head at the moment, as, like I've said before, am totally new to rails.
User Controller:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def index
#user = User.find(:all)
end
end
Master Item View:
<div class="well">
<h1>All Items</h1>
<table>
<tr>
<th>Title</th>
<th>Details</th>
<th>Inquire</th>
<th></th>
<th></th>
<th></th>
</tr>
<% #items.each do |item| %>
<tr>
<td><%= link_to item.title, item_path(item) %></td>
<td><%= item.content %></td>
<td><%= mail_to item.email, "Inquire", :cc => "michaelomchenry#gmail.com",
:subject => "OverFlow Inquiry Regarding " + item.title %></td>
<td><%= link_to 'Show', item %></td>
<td><%= link_to 'Edit', edit_item_path(item) %></td>
<td><%= link_to 'Destroy', item, confirm: 'Are you sure?', method: :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Item', new_item_path %>
</div>
Item Model:
class Item < ActiveRecord::Base
attr_accessible :content, :user_id, :title
validates :content, :length => { :maximum => 140 }
belongs_to :user
delegate :email, to: :user
end
There is a lot of stuff there. First of all, to be able to filter the items (or the actions) based on the user, you need to know who is logged at the time, and so enable users to login. This could be done for example using Devise, an authorization gem for Rails. Devise is largely used and well documented.
Once Devise is setup, you can check whether the user is logged (for example using a before_filter), and Devise will create a "current_user" variable for you to use (this is shown in Devise tutorial). You can then use it to filter your item lists with something like :
editable_items = current_user.items
And then use the editable_items on your view. I would advise you to go read the Devise tutorial as what you are doing is a quite common and well documented task.
I would comment if I could, but I feel that this must be said in reference to the answer posted by #momchenr (answering their own question) as a follow up to the chosen answer.
#momcher wrote:
I ended up doing this:
<% if item.email == current_user.email %>
and it worked... is that ok?
Probably not. But that depends on how your system is set up. If users can edit their email addresses and/or email addresses aren't forced to be unique, they may be able to gain editing access to another user's "items" by changing their email address temporarily or just signing up as a new user with a known user's email address.
Even if you never display email addresses of users in your application, there is an inherent vulnerability when you leave that much of the authentication process in a user-provided field.
Not knowing exactly how things are set up in Devise—only based on the info you have provided—I'd try the following:
These two might be are a touch slower depending on the state of ActiveRecord when called
<% if item.user == current_user %>
<% if item.user.id == current_user.id %>
This one ought to be a touch faster since you are not getting the user object from the item object (you are just pulling directly from the user_id method of the user object)
<% if item.user_id == current_user.id %>
No matter if I am right or wrong in my guesses at speed, this is generally a better solution than the one you said works for you. Since the user's ID is never under their control directly—unless there are major holes in your code—they cannot easily pose as other users.
I ended up doing this:
<% if item.email == current_user.email %>
and it worked... is that ok?

My controller is -persistently- sending wrong params[:id] in Rails?!

I'm new to Ruby on Rails & to web programming.
In my application I have two models; Directorate which has_many :users, and User which belongs_to :directorate.
When creating a new user, I use <%= f.collection_select(:directorate_id,Directorate.all, :id, :name) %> in the new.html.erb form to assign the new user to specific directorate. However, I want to build a user-friendly interface for the dba that lists all directorates; and listing all users beside each directorate, with a link to assign any user to a specific directorate.
What I did is the following:
In Directorate model, I defined the following function:
def assign_user!(user)
user.update_attributes(directorate_id: #directorate)
end
and in the directorates controller, I defined the following action:
def assign_user
#directorate = params[:directorate]
assign_user! params[:user]
redirect_to directorates_url
end
Now, directorates/index.html.erb contains the following:
<h1>Listing directorates</h1>
<table>
<tr>
<th>Name</th>
<th>Info</th>
</tr>
<% #directorates.each do |directorate| %>
<tr>
<td><%= directorate.name %></td>
<td><%= directorate.info %></td>
<td><%= link_to 'Show', directorate %></td>
<td><%= link_to 'Edit', edit_directorate_path(directorate) %></td>
<td><%= link_to 'Destroy', directorate, confirm: 'Are you sure?', method: :delete %></td>
<%= #directorate = directorate%>
<%= render 'users_form' %>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New Directorate', new_directorate_path %>
and, -users_form.html.erb contains the following form (which is supposed to list all users beside each directorate, with a link to assign any user to a certain directorate):
<h1>Listing Users</h1>
<table>
<tr>
<th>User Name</th>
</tr>
<% #users.each do |user| %>
<tr>
<td><%= user.username %></td>
<td><%= link_to 'Assign to Current Directorate', {controller: 'directorates', action: 'assign_user', directorate: #directorate, user: user}, :method => :put %></td>
</tr>
<% end %>
</table>
<br />
Here is the problem, when listing directorates & click on the 'Assign to Current Directorate' I receive the following error:
http://127.0.0.1:3000/directorates/assign_user?directorate=4&user=5
ActiveRecord::RecordNotFound in DirectoratesController#update
Couldn't find Directorate with id=assign_user
Rails.root: /home/ehab/sites/IAMS
Application Trace | Framework Trace | Full Trace
app/controllers/directorates_controller.rb:61:in `update'
Request
Parameters:
{"_method"=>"put",
"authenticity_token"=>"L5tz3hv2IW0meE79qUq0/tjfGKwDlpC23hOeAWtmTvk=",
"directorate"=>"4",
"user"=>"5",
"id"=>"assign_user"}
It's clear that the params is submitting "id"=>"assign_user" which I don't want, what i want is "id"=>"directorate.id" (4 in the above example). What shall I do to fix this issue?!
first of all your routes should say that assign_user is a member method on a certain directorate object:
resources :directorates do
member do
put :assign_user
end
end
second you say you define assign_user! in Directorate model and assign_user in DirectoratesController but both methods imply that they share same object state like instance variable #directorate which is not true
your controller method assign_user should look vaguely like
def assign_user
#directorate = Directorate.find params[:id]
#user = User.find params[:user_id]
#directorate.assign_user! #user
end
and model method should look like
def assign_user!(user)
user.update_attributes(directorate_id: self.id)
end
and even that i would switch around to instead of telling Directorate to change user's attributes you would tell User to assign itself to whatever controller wants.
and the final bit is your link that assigns user to directorate:
link_to 'Assign to Current Directorate',
assign_user_directorates_path(#directorate, :user_id => user)
0 lines of code above were tested for even syntactical correctness, DO NOT copy-paste, read and understand

Rails Contacts App contact delete

I am trying to simply create a button that will delete a contact from a list of contacts.
At the moment I have the following setup:
Contacts Controller
def destroy
#user.contacts.delete(params[:contact])
#contact.delete
end
View
<tbody>
<% #contacts.each do |contact| %>
<tr>
<td><%= contact.name %></td>
<td><%= contact.company %></td>
<td><%= contact.email %></td>
<td><%= contact.phone %></td>
<td><%= contact.mobile %></td>
<td><%= button_to 'Delete', contact, :method => :delete %></td>
</tr>
<% end %>
Routes
controller :contact do
get "newcontact" => "contact#new"
get "index" => "contact#index"
delete "delete" => "contact#destroy"
end
I have read online that using button_to is the preferred method but at the moment I am getting the following error:
undefined method `contacts' for nil:NilClass
It would be great to get any feedback that might help me fix this.
Thanks in advance
Tom
For anyone looking for the final answer on this it was
def destroy
contact = current_user.contacts.find(params[:id])
contact.destroy
redirect_to index_path
end
In my controller and the following in my view
<%= button_to 'Delete', contact, :method => :delete %>
Thanks
Tom
If you are goin to use #user you need to set #user in your delete method or else it will be nil - hence the error message
The other issue is that the id of the contact is params[:id] and not params[:contact]
I'd just do
contact = current_user.contacts.find(params[:id])
contact.destroy
You definitely want to be deleting the contact via the user or else people can delete other users' contacts. This deletes the contact and removes it from the association - you don't need to do anything else
try something like this:
button_to "Delete", { :action => "delete", :id => contact.id}, :method => :delete
If #user is only defined in create it's only going to exist for requests that call create—and I'm guessing you're not calling create right before destroy. Each browser request creates a new instance of the controller; instance variables like #user don't stick around between requests.
To make it work, you probably just need this:
def destroy
Contact.delete params[:contact]
end
(When you delete a contact it will automatically be removed from e.g. current_user.contacts assuming your relations are set up in the usual way.)

NoMethodError in Bank_accounts#show

I'm attempting to learn Ruby on Rails by creating a project and I can't seem to get my head around an error I keep getting. If I don't get the "NoMethodError", I'll get something like "You have a nil object when you didn't expect it!" - I simply want to view a bank account and have it show the transactions on that bank account. My problem may be with the controller, but I've tried all sorts of different things and can't figure it out. Much help would be greatly appreciated. I think I've included everything, if not, I apologise and will include what ever else is needed.
Bank Account Controller Show (I think the error is in the show):
def show
#bank_account = BankAccount.find(params[:id])
#transactions = #bank_account.transaction
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #bank_account }
end
Views: bank_accounts>show.html.erb
<% #transactions.each do |transaction| %>
<tr>
<td><%=h transaction.transaction_id %></td>
<td><%=h transaction.dateD %></td>
<td><%=h transaction.trans_type %></td>
<td><%=h transaction.amount %></td>
<td><%=h transaction.new_balance %></td>
<td><%=h transaction.transaction_success %></td>
<td><%= link_to 'Show', transaction %></td>
<td><%= link_to 'Edit', edit_transaction_path(transaction) %></td>
<td><%= link_to 'Destroy', transaction, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
I think this is the backtrace (sorry for not including it).
"BankAccount/app/controllers/bank_accounts_controller.rb:17:in `show'"
Also, if I do the following for the bank account controller:
def show
#bank_account = BankAccount.find(params[:id])
#transactions = #bank_account.transactions
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #bank_account }
end
I get this SQL error:
"ActiveRecord::JDBCError: [SQLITE_ERROR] SQL error or missing database (no such column: transactions.bank_account_id): SELECT * FROM "transactions" WHERE ("transactions".bank_account_id = 1)"
It looks like your problem is that the BankAccount.find is returning nil. Then, on the next line, you are trying to access the transaction method on #bank_account, which throws the error.
Make sure that params[:id] has the correct value.
if your BankAccount model has many transactions, the method to access them is plural:
#transactions = #bank_account.transactions
You may want to look at your model code and your migration code.
In order to have #bank_account.transaction to return the transaction, the two of them must have one-to-one relationship.
Your bank_account model should have
has_one :transaction
Your transaction should have
belong_to :bank_account
Your migration script when creating transaction table should have something like
transaction.references :bank_account
The reference will create the field bank_account_id in your transaction table.
I think it explain why you have error that "no such column: transactions.bank_account_id)"

Resources