undefined method `lessons' for true:TrueClass - ruby-on-rails

I want users to be able to view a task's submit button if and only if they have completed the task's required courses. Right now, I added a line, current_user.complete(#task.courses.all?) to my tasks/show.html.erb page that should only allow a user to view the submit button if they have completed the task's courses. But this line throws an error at the def complete user method saying that the method lessons is undefined for true:TrueClass.
Task Model:
has_many :submissions
has_and_belongs_to_many :courses
Submission Model:
belongs_to :user
belongs_to :task
Course Model:
has_many :lessons, dependent: :destroy
has_many :users, through: :enrolments
has_and_belongs_to_many :tasks, optional: true
Lesson Model:
belongs_to :course
has_many :views
has_many :users, through: :views
User Model:
has_many :courses, through: :enrolments
has_many :submissions
has_many :views
has_many :lessons, through: :views
def view(lesson)
lessons << lesson
end
def viewed?(lesson)
lessons.include?(lesson)
end
def complete(course)
lessons.where(course: course).ids.sort == course.lessons.ids.sort
end
Task/Show.html.erb:
<% if current_user.complete(#task.courses.all?)%>
<%= link_to "Submit", new_task_submission_path(#task), class: "btn btn-primary" %>
<% end %>

Your complete method expects course as a parameter, and it tries to call course.lessons on it.
You are calling
current_user.complete(#task.courses.all?)
which means you are passing a boolean into complete instead of a course.
Perhaps you meant something like:
#task.courses.all? { |course| current_user.complete(course) }
Aleksei Matiushkin suggests that it would be more efficient to use something like:
current_user.joins(:courses).joins(:lessons).where(complete: false).count == 0

Related

Mass edit (create/destroy) many2many relation on Rails

I'm stucked in the way to do this task on Rails 5.
I need to "mass" edit a relation between a Project and it's Members
In the UI, I open a popup with a certain list of members (a list of User) and those member have or have not belongs to the Project (check the relations down here).
I need to have the ability to mark some of them (who not belongs to) or unmark others (who belongs to) with checkbox's and "save" the form and create/delete the relations.
For the record I have this models
// project.rb
class Project < ApplicationRecord
has_many :memberships, dependent: :destroy, class_name: 'ProjectMember'
has_many :members, through: :memberships, class_name: 'User', source: :user
end
// project_member.rb
class ProjectMember < ApplicationRecord
belongs_to :user
belongs_to :project
end
// user.rb
class User < ApplicationRecord
has_many :project_members
has_many :projects, through: :project_members
end
I'm kinda new on Rails and I'm really stucked in the way to create the forms (using SimpleForms) and how to edit the relation.
What's the correct approach? I tried to find over the web without success :(
I hope my question it's clear enough :)
I think the helpers you want are included in the gem cocoon : https://github.com/nathanvda/cocoon
Using this gem, your view for the project edition would look like this :
projects/_form
= simple_form_for #project do |f|
-# your project fields...
%h3 Members
#members
= f.simple_fields_for :members do |member|
= render 'member_fields', f: member
.links
= link_to_add_association 'add member', f, :members
= f.submit
projects/_member_fields
.nested-fields
= f.association :user
= link_to_remove_association "remove user from project", f
Edit : I focused on the view part, but before that, in the model, you have to accept nested attributes from forms :
class Project < ApplicationRecord
has_many :memberships, dependent: :destroy, class_name: 'ProjectMember'
has_many :members, through: :memberships, class_name: 'User', source: :user
accepts_nested_attributes_for :members, reject_if: :all_blank, allow_destroy: true
end
There's also a bit of work to do in the controller to permit the nested attributes.
def project_params
params.require(:project).permit(:name, :description, members_attributes: [:id, :user_id, :_destroy])
end

rails 4 multiple ruby arrays with nested attributes

I'm trying to build a method that pulls an array of arrays, much like nested loops in the view.
I am trying to build a method on my User model that does this:
#past_parties = User.parties
<%= #past_parties.each do |past_party| %>
<%= past_party.guests("email").uniq.each do |guest| %>
<%= guest.name %> <%= guest.email %>
<% end %>
<% end %>
These are my models:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
def past_guests
self.parties.guests
end
end
class Host < ActiveRecord::Base
belongs_to :user
has_many :parties, dependent: :destroy
has_many :guests, through: :parties
end
class Party < ActiveRecord::Base
belongs_to :host
has_many :guests, dependent: :destroy
end
class Guest < ActiveRecord::Base
belongs_to :party
end
I can get the loop to work in the view, but when I try to build it as a method (so I can call current_user.past_guests, if current_user is an instance of User.), I get this error
undefined method `guests' for #<ActiveRecord::Associations::CollectionProxy []>
How do I get all of the guests that a user has ever invited without building the loop in the view?
The problem is that you're trying to access an array from another array:
self.parties.guests
self.parties returns an #<ActiveRecord::Associations::CollectionProxy []>, so if you want to get the guests of the parties you have to loop over the elements.
But since you want only the guests, you can simply change your user class to:
class User < ActiveRecord::Base
has_many :hosts, dependent: :destroy
has_many :parties, through: :hosts
has_many :guests, through: :parties
# Call your guests
def past_guests
self.guests
end
end
Each party is going to have collection of guests. You need to do this in your method definition:
def past_guests
self.parties.collect(&:guests)
end

Rails Checking if One Array is in Another

I have five models: Course, Lesson, Question, Answer and User.
What I'm trying to do is determine if the User has Answers for all of the Questions in a Lesson (so I can put "Done" next to the lesson in the view if this is the case).
My models:
class Course < ActiveRecord::Base
has_many :lessons, dependent: :destroy
has_many :questions, :through => :lessons
has_many :users, through: :purchases
end
class Lesson < ActiveRecord::Base
belongs_to :course
has_many :questions, dependent: :destroy
has_many :answers, through: :questions
end
class Question < ActiveRecord::Base
belongs_to :lesson
belongs_to :course
has_many :answers, dependent: :destroy
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
class User < ActiveRecord::Base
has_many :answers
has_one :author
has_many :courses, through: :purchases
end
What I tried to do was to check if a Lesson's Questions were in the Questions the User Answered, but the includes? line doesn't seem to be working the way I want.
in my controller, I have:
#lessons = #course.lessons
#answers = current_user.answers
#questions = Question.where(:id => #answers.map(&:question_id))
in my view, I have:
<% #lessons.each do |lesson| %>
<% lesson_questions = lesson.questions %>
<%= user_questions = #questions.where("lesson_id = ?", lesson.id)%>
<% if user_questions.include?(lesson_questions)%>
Done!
<% end %>
<% end %>
I'm not sure if this is the cause, but I noticed the lesson_questions are #<Question::ActiveRecord_Associations_CollectionProxy:0x9c49698>
While the user_questions are: #<Question::ActiveRecord_Relation:0x9c48330>
I'm wondering, (a) how I accomplish my objective of finding the Lessons with all of the Questions answered, and (b) if there's a more efficient way to do this. Thanks!
Problem
You can't check if an array includes another array just like this:
user_questions.include?(lesson_questions)
You need to check if each element from lesson_questions is included in the user_questions.
Try these instead:
Solution: 1
lesson_questions.all? { |lq| user_questions.include?(lq) }
This should return true if all the lesson_questions are included in the user_questions.
Solution: 2
(lesson_questions - user_questions).empty?

How do I eager load two levels of associations in 1 call?

I have FamilyTree, Node, Comment, & User models.
The relationship is like this:
FamilyTree
class FamilyTree < ActiveRecord::Base
belongs_to :user
has_many :memberships, dependent: :destroy
has_many :members, through: :memberships, source: :user, dependent: :destroy
has_many :nodes, dependent: :destroy
end
Node
class Node < ActiveRecord::Base
belongs_to :family_tree
belongs_to :user
has_many :comments, dependent: :destroy
end
Comment
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :node
end
User
class User < ActiveRecord::Base
has_one :family_tree, dependent: :destroy
has_many :memberships, dependent: :destroy
has_many :nodes, dependent: :destroy
has_many :comments
end
Membership ## This is just to store the user memberships on various family_trees
class Membership < ActiveRecord::Base
belongs_to :family_tree
belongs_to :user
end
In my Dashboard#IndexController where I am using this, I have:
def index
#family_tree = current_user.family_tree
#nodes = #family_tree.nodes.includes(:comments)
#node = current_user.nodes.new
#memberships = current_user.memberships.limit(3)
end
When I am trying to optimize my app with the Bullet gem, I get this message:
N+1 Query detected
Comment => [:user]
Add to your finder: :include => [:user]
N+1 Query method call stack
My _comments partial that is generating this N+1 issue is called like this - in my views/dashboard/index.html.erb:
<% #nodes.each do |node| %>
<%= render partial: "shared/comments", locals: {node: node} %>
<% end %> <!-- node -->
This is where the n+1 offending queries occur, around these lines in my _comments partial.
<% node.comments.each do |comment| %>
<li class="clearfix">
<a class="avatar" href="#">
<%= image_tag(comment.user.avatar.url)%>
So it seems the solution is to optimize my controller call, but I am not quite sure how to do 2-levels of association. I tried this:
#nodes = #family_tree.nodes.includes(:comments).includes(:user)
But that doesn't seem to get rid of the N+1 query problem.
Any ideas?
You have to pass a hash to the includes
#nodes = #family_tree.nodes.includes(:comments => :user)

NoMethodError? Counting Records

I have the following models:
class Label < ActiveRecord::Base
has_many :releases
end
class Release < ActiveRecord::Base
belongs_to :label
has_many :products
has_and_belongs_to_many :tracks
def self.releases_count
self.count(:all)
end
end
class Product < ActiveRecord::Base
belongs_to :release
has_many :releases_tracks, :through => :release, :source => :tracks
has_and_belongs_to_many :tracks
def self.products_count
self.count(:all)
end
end
On my label/index view i'm able to display a count of the Releases absolutely fine using:
<%= label.releases.releases_count %>
I'm trying to do the same for Products using:
<%= label.releases.products.products_count %>
But get a NoMethodError:
undefined method `products' for #<Label:0x10ff59690>
Any ideas?
I have lots of other aggregations I want to perform (Track Counts etc) so some guidance on where I'm going wrong would be really appreciated.
You need define your production/Label association
class Label < ActiveRecord::Base
has_many :releases
has_many :products, :through => :releases
end

Resources