query in rails3 - ruby

In another question that i asked recently i got a really good answer and the code worked... But i do not know exactly why it works... Now i have a similar problem, but don't know how to solve it...?
What i have:
Models
users
questions (with answer_id)
answers
votes (with answer_id and user_id)
model for users:
has_many :questions
has_many :votes
def can_vote_on? (question)
!question.answers.joins(:votes).where('votes.user_id = ?', id).exists?
end
def voted_answer? (question)
(what to do here...?)
end
model for questions:
belongs_to :user
has_many :answers, :dependent => :destroy
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:text].blank? }, :allow_destroy => true
model for answers:
belongs_to :question
has_many :users, :through => :votes, :dependent => :destroy
has_many :votes
model for votes:
belongs_to :answer
belongs_to :user
In my question view i want to make a text bold when the current_used has voted on that specific answer. So how do i finish this:
<% for answer in #question.answers %>
<% if current_user.voted_answer? (#question) %>
<td>
<strong><%= answer.text %></strong>
</td>
<% else %>
<td>
<%= answer.text %>
</td>
<% end %>
<% end %>
Thijs

you may do this
<% for answer in #question.answers %>
<% if answer.votes.index{|vote| vote.user_id == current_user.id} %>
<td>
<strong><%= answer.text %></strong>
</td>
<% else %>
<td>
<%= answer.text %>
</td>
<% end %>
<% end %>
UPDATE
more logical variant create voted_by_user? function in class Answer
class Answer
def voted_by_user?(user)
voits.where('votes.user_id = ?', user.id).exists?
end
end
<% #question.answers.each do |answer| %>
<td>
<% if answer.voted_by_user?(current_user) %>
<strong><%= answer.text %></strong>
<% else %>
<%= answer.text %>
<% end %>
</td>
<% end %>

It sounds like you just want the opposite result of can_vote_on?, i.e. if a user cannot vote on an answer (can_vote_on? returns false), then it means that they already voted (voted_answer? should return true in this case) and vice versa.
One way to solve this is to have voted_answer? return the negation of can_vote_on:
def voted_answer? (question)
!can_vote_on? question
end
Or of course you could use the query you used in can_vote_on? without the negation:
def voted_answer? (question)
question.answers.joins(:votes).where('votes.user_id = ?', id).exists?
end
But I would prefer the first solution due to the DRY principle.
UPDATE
I was wrong about the negation. In this case you're dealing with a specific answer, not all of them.
In your model you'll want the following:
def voted_answer? (answer)
answer.votes.where('votes.user_id = ?', id).exists?
end

Related

LoadError: Unable to autoload constant Review, Failure/Error: #review = Review.new

I am getting the following error in my tests in this Ruby project I am doing. I can't seem to figure out why.
Below is my controller.
class ReviewsController < ApplicationController
def new
#restaurant = Restaurant.find(params[:restaurant_id])
#review = Review.new
end
def create
#restaurant = Restaurant.find(params[:restaurant_id])
#restaurant.reviews.create(review_params)
end
private
def review_params
params.require(:review).permit(:thoughts, :rating)
end
end
Below is my view for the Reviews.
<%= form_for [#restaurant, #review] do |f| %>
<%= f.label :thoughts %>
<%= f.text_area :thoughts %>
<%= f.label :rating %>
<%= f.select :rating, (1..5) %>
<%= f.submit 'Leave Review' %>
<% end %>
I can't seem to figure out what is wrong.
The model for the Review is wrong. My model was referring to the wrong class.
class Restaurant < ApplicationRecord
belongs_to :restaurant
end
When it should have been the following.
class Review < ApplicationRecord
belongs_to :restaurant
end

Artists Group_by nested attribute Order_date

I have created a functioning e-commerce platform where Members can buy songs. Everything works fine, But I would like to group all of my Orders in my Index Page by Month.
Currently I am able to group each Album with its corresponding Artist, and each Ordered Song to its corresponding Album. But now I would like to group Orders by Month.
How Can I Group Artists by the order_date in my Orders Table, So that everything is organized by Month?
Ex. of what I've like to do
Month 1 Orders
Artist1
Album1 ###List of Albums Corresponding to an Artist
--Song1 (10 0rders)
--Song3 (5 Orders)
Album2
--Song5 (2 Orders) ###Ordered Songs Corresponding to an Album
Month 2 Orders
Artist2
Album1
--Song2 (1 Order)
Artist3
Album3
--Song5 (1 Order)
MODELS
class Order < ActiveRecord::Base
attr_accessible :artist_id, :album_id, :song_id, :user_id, :order_date
belongs_to :song
belongs_to :user
end
class Artist < ActiveRecord::Base
attr_accessible :name
has_many :albums
has_many :songs, :through => :albums
has_many :orders, :through => :songs
end
class Album < ActiveRecord::Base
attr_accessible :name, :artist_id
belongs_to :artist
has_many :songs
has_many :orders, :through => :songs
end
class Song < ActiveRecord::Base
attr_accessible :artist_id, :album_id, :title, :price
belongs_to :album
has_many :orders
end
CONTROLLERS
###How Can I Group Artists by the order_date in my Orders Table?
def index
#artists = Artist.includes(:orders).where('orders.id IS NOT NULL')
end
VIEWS
<% #artists.each do |artist| %> ###Lists All Artists with Orders
<h3><%= artist.name %></h3>
<% artist.albums.each do |album| %> ###Lists All Albums corresponding to Artist
<h4><%= album.name %></h4>
<% album.songs.each do |song| %> ###Lists All Ordered Songs corresponding to Albums
<% if song.orders.count >= 1 %>
(<%= song.orders.count %>)
<%= song.title %>
$<%= song.price %><br>
<% end %>
<% end %>
<% end %>
<% end %>
You get the list of orders ok with this line:
<% #order_months.sort.each do |month, orders| %>
But then you jump back into your full list of artists with this line:
<% #artists.each do |artist| %> ###Lists All Artists with Orders
Instead I think you need something like
<% orders.each do |order| %>
<% order.artists.each do |artist| %>
<% artist.albums.each do |album| %>
<% album.songs.each do |song| %>
<%= song.name %>
To get order.artists you will need to modify your model:
class Order < ActiveRecord::Base
attr_accessible :artist_id, :album_id, :song_id, :user_id, :order_date
belongs_to :song
belongs_to :user
has_many :albums, through: :song
has_many :artists, through: :albums
end
This is how I ended up Solving the Problem
Controller
#orders = Order.find(:all, :order => 'order_date, id', :limit => 50)
Views
<% #orders.sort.group_by { |order| order.order_date.beginning_of_month }.each do |month, orders| %>
<h3><%= month.strftime('%B') %> </h3>
<% orders.group_by { |order| order.song.album.artist }.each do |artist, orders| %>
<h4><%= artist.name %> </h4>
<% orders.group_by { |order| order.song.album }.each do |album, orders| %>
<h5><%= album.name %> </h5>
<% orders.group_by { |order| order.song }.each do |song, orders| %>
<p>(<%= orders.count %>) <%= song.title %> </p>
<p><%= song.price %> </p>
<% end %>
<% end %>
<% end %>
<% end %>

Nested attributes that do not belong to the current object using has_many :through

My end goal is to be able to add costumes to an agreement in the agreement view, regarless of whether or not they exist in the Costume database yet. My main difficulty is that a costume does not belong to an agreement, they exist independently but can be added to an agreement. If a new costume is added that isn't in the Costume database, it will add it to the database. Is there a way to do this? I can't find a tutorial about this anywhere. If I could get the controller from this post, I think that is all I need. I just need to create one costume every time the form is displayed. Thanks so much.
My models are as follows:
# app/models/agreement.rb
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :costumes, join_table: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end
# app/models/costume.rb
class Costume < ActiveRecord::Base
has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end
# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
belongs_to :agreement
belongs_to :costume
end
My agreement controller is as follows:
class AgreementsController < ApplicationController
before_action :set_agreement, only: [:show, :edit, :update, :destroy]
# Some methods ommitted
# GET /agreements/new
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costume.all
end
private
# Use callbacks to share common setup or constraints between actions.
def set_agreement
#agreement = Agreement.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
end
:end
And finally, the agreement view
<%= form_for(#agreement) do |f| %>
<% if #agreement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>
<ul>
<% #agreement.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Agreement fields omitted -->
<%= f.fields_for :costumes do |c| %>
<div class="field">
<%= c.label :cid %><br>
<%= c.number_field :cid %>
</div>
<div class="field">
<%= c.label :description %><br>
<%= c.text_field :description %>
</div>
<!-- etc with costume fields -->
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You need accepts_nested_attributes_for in your Agreement controller if you want to create new costumes there.
class Agreement < ActiveRecord::Base
has_many :agreement_costumes
has_many :costumes, through: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end
and then in the agreements#new action in your Agreements controller you build a costume entry
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costumes.all
end
#agreement.costumes.build creates a blank instance of a costume, related to this agreement. You then access the params of that costume in the form using :costumes Don't forget to whitelist your nested params in your Agreements controller:
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
:title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end
Now your form has to have a place to choose existing costumes from a list and/or add a new one.
<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>
<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
<%= c.label :name %>
<%= c.text_field :name %>
<br>
<%= c.label :price %>
<%= c.number_field :price %>
<br>
<% end %>
This should get you most of the way there. I've had to guess at some of your code, so this isn't going to be a cut and paste answer. You'll need to build what you can off of this and probably do a little more Googling here and there. If you wanted to have a popup form to add a costume to the list on the fly and then be able to choose it in the collection_select, you would have to turn on Turbo_links in your app, create a Javascript popup form. Then use AJAX to submit the form, save the costume to the database, run another .js.erb file that would then update the collection_select text list using a reload of just that list via your Javascript. It's actually probably easier than having a new costume form nested in this form.

Ruby - Nesting objects through input values

Hello my fellow companion!
What I'm trying to achieve is a system by which an Order form is compiled in two ways:
by fullfilling its own attributes (:sender_name, :sender_mobile etc..)
by selecting products through the price labels attached on them.
After a while of poking here and there, I managed to display the product list on the order form. Here the 3 models and the views
models/order.rb
class Order < ActiveRecord::Base
attr_accessible :sender_comment, :sender_email, :sender_mobile, :sender_name, :order_attributes
has_many :products
accepts_nested_attributes_for :products
end
models/product.rb
class Product < ActiveRecord::Base
belongs_to :order
attr_accessible :product_name, :product_description, :prices_attributes, :order_id
has_many :prices
accepts_nested_attributes_for :prices
end
models/price.rb
class Price < ActiveRecord::Base
belongs_to :product
attr_accessible :product_id, :price_label, :price_amount, :price_checked, :how_many_prices, :products_attributes
end
views/orders/_form.html.erb
<%= form_for(#order) do |f| %>
<div class="field">
<%= f.label :sender_name %><br />
<%= f.text_field :sender_name %>
</div>
# [...] other order's fields...
<%= f.fields_for :product do |builder| %>
<%= render "products_field", :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
views/orders/_products.html.erb
<% #products.each do |p| %>
<td><%= p.product_name %></td>
<td><%= p.product_description %></td><br />
<% p.prices.each do |price| %>
<td><%= price.price_label %></td><br />
<td><%= price.price_amount %></td><br />
<td><input type="radio" class="order_bool" name="<%= p.product_name %>" <% if price.price_checked == true; puts "SELECTED"; end %> value="<%= price.price_amount%>"/></td><br />
<% end %>
<% end %>
Products and relatives prices are printed in the Order form, yet once selected they're not saved as order_attributes; along with the order's attributes, every radio selected generates an object like this
[#<Product id: nil, order_id: 23, product_name: nil, product_description: nil, created_at: nil, updated_at: nil>]
How can I convert the selected products into effective order_attributes?
This is my first project with OOP and that I'm learning all by myself, with a very few help but from the internet. Please don't be too harsh!
Also feel free to change the title if you don't consider it appropriate enough; english is not my native language and i find very difficult to recap this issue in just a few words.
Thanks for the patient :)
Basically your current _products_fields partial isn't even attached to the form object. You're building arbitrary inputs right now. See form_for documentation and fields_for documentation and radio_button documentation.
Note: When you use f.method_name you're actually going to call the FormBuilder versions of the method and not the FormHelper versions. Since the FormHelper version has more and still relevant documentation that's the better version. Just omit the object_name parameter.
I think changing a these lines should do the trick:
Form Partial:
<%= f.fields_for :products, #products do |builder| %>
<%= render "products_field", :f => builder %>
<% end %>
Products Fields Partial
# 'f' is the variable passed in using ':f => builder'
# So 'f' = builder
# f.object is accessing the object were building the fields for
<td><%= f.object.product_name %></td>
<td><%= f.object.product_description %></td><br />
<% f.object.prices.each do |price| %>
<td><%= price.price_label %></td><br />
<td><%= price.price_amount %></td><br />
<td><%= f.radio_box("price", price.price_amount %></td><br />
<% end %>

Adding comment as "most commented" on my view?

I have a Rails 3 blog, which has Article and Comment models in has_many and belongs_to associations. When an article comment is > 3, I should see it in my application.html.erb views so I can call it "most commented".
<div class="span2">
<%=image_tag ("Lekki_Gardens_new.gif") %>
<br><br>
<b><p>News update</p></b>
<% #articles.first(4).each do |article| %>
<%=image_tag article.avatar_url(:lilthumb).to_s, :class=>"img-polaroid" %><br>
<%= link_to article.name, article%><hr>
<% end %>
</div
You can use the :counter_cache option in your article model and then use a scope to retrieve the most commented ones.
class Article < ActiveRecord::Base
has_many :comments, counter_cache: true
scope :most_commented, where('comments_count > 3')
end
And then in your template:
<% Article.most_commented.each do |article| %>
<% # anything you want %>
<% end %>

Resources