Trying to design a controller to display courses (Rails) - ruby-on-rails

Below are the models that are relevant to my problem. I am attempting to design a way to display CourseEnrollments along with their steps to a given patient. Here is what I have come up with so far.
INDEX ACTION - /course_enrollments/ --> Display courses user is enrolled in as well as the most recent course overview. This can redirect to most recent course.
SHOW ACTION - /course_enrollments/:id --> Display courses user is enrolled in as well as the most recent course overview
The part I am struggling to figure out is how to display an individual step for a course. Should this be done in the course_steps controller (which is nested inside the courses resource)?
class Course < ActiveRecord::Base
belongs_to :course_category
belongs_to :client
belongs_to :user_created, :foreign_key => :user_created_by, :class_name => "User"
belongs_to :user_updated, :foreign_key => :user_last_updated_by, :class_name => "User"
has_many :course_steps, :dependent => :destroy
has_many :steps, :through => :course_steps
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :patients, :through =>:course_enrollments
end
class CourseStep < ActiveRecord::Base
belongs_to :step
belongs_to :course
validates_uniqueness_of :step_id, :scope => :course_id
end
class Step < ActiveRecord::Base
belongs_to :step_type
belongs_to :client
has_one :step_quiz, :dependent => :destroy
has_one :step_survey, :dependent => :destroy
has_one :step_text, :dependent => :destroy
has_one :step_download, :dependent => :destroy
has_one :step_video, :dependent => :destroy
has_one :step_presentation, :dependent => :destroy
has_many :course_steps, :dependent => :destroy
has_many :courses, :through => :course_steps
end
class CourseEnrollment < ActiveRecord::Base
belongs_to :patient
belongs_to :course
end
class Patient < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :enrollments, :dependent => :destroy
has_many :clients, :through => :enrollments
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :courses, :through => :course_enrollments
end

The usual approach is to nest these and have a compound sort of path, but how you route these things is often dependent on the level of context involved. For instance, is the display of a course driven by a user to the degree that the path should have the user in it, or is it a case of simply personalizing the course page?
Typically you see things like this:
resources :courses do |course|
course.resources :enrollments
course.resources :steps
end
There's usually a correlation between your has_many relationships and declaring an equivalent resources but not always.
Have a look at the generated routes using rake routes to see what the parameters will be called when passed to your controller, as well as what the expected controller name will be. You can customize the name of the controller by passing in a :controller option to the route.
Typically the last record in the path is passed in as :id whereas the prior ones are named, such as :course_id. This is a somewhat annoying inconsistency, so do be careful to check you're loading using the correct parameters.

If you need to display an individual step for a course, you definitely need to use the course_steps controller, logic being, each CourseStep object is a combo of one-course and one-step.

Related

after_destroy not called for linked table

I have these models
class User < ActiveRecord::Base
has_many :user_functions, :dependent => :destroy
has_many :functions, :through => :user_functions
accepts_nested_attributes_for :functions, allow_destroy: true
Model of the linked table:
class UserFunction < ActiveRecord::Base
belongs_to :user, inverse_of: :user_functions
belongs_to :function, inverse_of: :user_functions
after_destroy :unplan_items
after_create :plan_items
and of course the model of function but this is like user...
Now when I do the following in my tests:
#user.functions = [#functions]
#user.save
expect(#user.planned_items.count).to eq(1)
#user.functions = []
#user.save
I notice the callback after_destroy isn't called. Why is this and how can I avoid this. There are certain steps that need to be done every time a UserFunction is destroyed...
I believe this has to do with: https://github.com/rails/rails/issues/7618 (I'm using rails 4.2.5 though). The after_create is working perfect though...
Currently rails uses :delete_all as default strategy of has_many_through. It only calls :destroy_all when we explicitly specify dependent: :destroy on the association.
The docs mention advice to use has_many :through if you need callbacks:
See the suggestion here: http://guides.rubyonrails.org/association_basics.html
You should use has_many :through if you need validations, callbacks,
or extra attributes on the join model.
So there currently is an inconsistency between after_create which does do the callback and after_destroy.
This is mentioned in these two issues posted on GitHub:
https://github.com/rails/rails/issues/7618
https://github.com/rails/rails/issues/27099
The fix for now is to explicitly put :dependent => :destroy on the :through part. This will make sure the callback are used.
class User < ActiveRecord::Base
has_many :user_functions
has_many :functions, :through => :user_functions, :dependent => :destroy
accepts_nested_attributes_for :functions, allow_destroy: true
For anyone reading this 2021+
Change This
has_many :object_tags, :as => :taggable, :dependent => :destroy
has_many :tags, :through => :object_tags
To This
has_many :object_tags
has_many :tags, :through => :object_tags, :dependent => :destroy

has_many through in Ruby on Rails app

In my rails app a user can create courses and schools (group). Each time a user creates a course or school their user_id is stored in the database table for the course or school, so a user has_many :schools and :courses and the school or course belongs_to :user. Also, the user can can attend courses and join schools (as student or professor) with a has_many through relationship model (schoolgroups for schools which has a :user_id, school_id, and :role [string], and for courses student_users (and professor_users) which has a :user_id, course_id, and :role [string]. My question is, in the user model can I just specify once that:
has_many :schools, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
has_many :student_users, :dependent => :destroy
has_many :professor_users, :dependent => :destroy
or would I have to have a user model that looked like this:
has_many :schools, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
has_many :schools, :through => :schoolgroups
has_many :student_users, :dependent => :destroy
has_many :courses, :through => :student_users
has_many :professor_users, :dependent => :destroy
has_many :courses, :through => :professor_users
You'll need to think about the ownership of the models in a bit more detail. has_many means a many-to-one relationship, so saying Professor has_many Courses means that the professor owns the courses and the course only has one professor (or does it?). So you'll do something like this:
has_many :school, :dependent => :destroy
has_many :courses, :dependent => :destroy
has_many :schoolgroups, :dependent => :destroy
On the other hand, you'll have things that the user is associated to, but other users may also be associated in the same way. For example, a user can be a student in a course, but so can many other students. For this you'll want to use has_and_belongs_to_many or HABTM which represents a many-to-many relationship:
has_and_belongs_to_many :courses_as_student, :class_name => "Course",
:join_table => "student_users"
Then in the Course class:
belongs_to :user
# or even better:
# belongs_to :professor, :class_name => "User", :foreign_key => "professor_id"
has_and_belongs_to_many :students, :class_name => "User",
:join_table => "student_users"
You can read all the details in the Rails documentation.

Trouble on deleting "associated" model instances

I am using Ruby on Rails 3.1.0 and I would like to know why database records in the following example are not deleted on destroying an "associated" model instance.
In my application I have these classes:
class User < ActiveRecord::Base
has_one :account,
:autosave => true,
:dependent => :destroy
has_many :article_relationships,
:class_name => 'ArticleUserRelationship',
:foreign_key => 'user_id',
:autosave => true,
:dependent => :destroy
has_many :articles,
:through => :article_relationships,
:source => :article,
:dependent => :destroy
...
end
class ArticleRelationship < ActiveRecord::Base
belongs_to :user
...
end
class Account < ActiveRecord::Base
belongs_to :user,
:autosave => true,
:dependent => :destroy
end
If I run #article.user.destroy or #article.user.account.destroy methods (note: article is kind of Article) those will delete from the database the user, the account and article relationship records but will not delete users related articles, even if I state has_many :articles ... :dependent => :destroy.
Anyway I read the official RoR guide about the "has_many Association Reference" and at the 4.3.2.6 :dependent chapter it says:
This option (:dependent) is ignored when you use the :through option on the association.
So, what could/should I make in order to delete all user articles? That is, what method could/should I run? How?

Is this the correct way to use way :dependent => :destroy in my RoR app?

This is how my RoR app is setup
note.rb
belongs_to :user
has_many :note_categories
has_many :categories, :through => :note_categories
category.rb
has_many :note_categories
has_many :notes, :through => :note_categories
I want to make it so that when a user deletes a note, the corresponding entry in the note_categories table is deleted as well. Do I use :dependent => :destroy to do that?
Also, if I wanted to make it so that if a user deletes a note, and that means that there are no more notes with the category it had, the category itself was deleted, how would I do that? Thanks for reading.
I want to make it so that when a user
deletes a note, the corresponding
entry in the note_categories table is
deleted as well. Do I use :dependent
=> :destroy to do that?
Yes, that's correct.
Also, if I wanted to make it so that
if a user deletes a note, and that
means that there are no more notes
with the category it had, the category
itself was deleted, how would I do
that?
You use an after_destroy callback.
class Note < ActiveRecord::Base
belongs_to :user
has_many :note_categories, :dependent => :destroy
has_many :categories, :through => :note_categories
end
class Category < ActiveRecord::Base
has_many :note_categories, :dependent => :destroy
has_many :notes, :through => :note_categories
end
class NoteCategory < ActiveRecord::Base
belongs_to :note
belongs_to :category
after_destroy { category.destroy if category.notes.empty? }
end

Rails: Multiple "has_many through" for the two same models?

Can't wrap my head around this...
class User < ActiveRecord::Base
has_many :fantasies, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasy < ActiveRecord::Base
has_many :users, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasizing < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... which works fine for my primary relationship, in that a User can have many Fantasies, and that a Fantasy can belong to many Users.
However, I need to add another relationship for liking (as in, a User "likes" a Fantasy rather than "has" it... think of Facebook and how you can "like" a wall-post, even though it doesn't "belong" to you... in fact, the Facebook example is almost exactly what I'm aiming for).
I gathered that I should make another association, but I'm kinda confused as to how I might use it, or if this is even the right approach. I started by adding the following:
class Fantasy < ActiveRecord::Base
...
has_many :users, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class User < ActiveRecord::Base
...
has_many :fantasies, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class Approval < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... but how do I create the association through Approval rather than through Fantasizing?
If someone could set me straight on this, I'd be much obliged!
Keep your first set of code, then in your User Model add:
has_many :approved_fantasies, :through => :fantasizings, :source => :fantasy, :conditions => "fantasizings.is_approved = 1"
In your Fantasizing table, add an is_approved boolean field.

Resources