Rails 3.2 association methods crashes server & console - ruby-on-rails-3.1

I am trying to upgrade a Rails 2.3.5 application to Rails 3.2.1. I know I should have done 3.0 and 3.1 first. However...
I'm experiencing a very strange problem when I try to access an associated model through an auto generated accessor method. For instance, I have User and Member models where User has one member and Member belongs to User. Calling User.first.member will crash the webrick server with no error message. The same happens in the console with the following output.
irb(main):003:0> User.first.member
←[1m←[36mUser Load (0.0ms)←[0m ←[1mSELECT `users`.* FROM `users` LIMIT 1←[0m
C:\git\web_benebridge\website>rails c
Both objects do exist in the database and are linked correctly and this works fine in Rails 2.3.5.
The User model is defined thus:
class User < ActiveRecord::Base
require 'digest/sha2'
belongs_to :association
has_one :member
...
And the Member model like this:
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :association
...
The Association model shown above starts its definition like:
class Association < ActiveRecord::Base
has_many :association_benefits
has_many :association_kases
has_many :benefits
has_many :contacts
has_many :content_items
has_many :members
has_many :users
...
I've tried various other models that have associations and some of the association accessor methods work but others do not. It's really bothersome that there is no error thrown at all, the environment just exits.
One concern I have is that having a model named Association may be confusing things. Has anyone else run into a problem with this in the later versions of Rails?
Any help resolving this will be greatly appreciated.

Related

One to many Rails relationship. Is has_many required (Rails 5)?

I'm trying to generate a one-to-many relationship between users and advice. One user can have many advices.
rails g model User
rails g model Advice user:references
I noticed that this doesn't automatically generate the line has_many :advices in the user.rb file. However, in the advice.rb file, the line belongs_to :user was auto-generated.
In the rails console, I am still able to create multiple Advices for one user without an error.
My question is, is the has_many :advices line necessary in the user.rb file? If not, then why do the Rails guides recommend it?
The has_many association tells Rails that the objects are related and adds methods to the object for querying the associated objects. You could live without it, but it makes things easier.
See the first chapter here:
http://guides.rubyonrails.org/v2.3.11/association_basics.html
Another reference:
https://apidock.com/rails/ActiveRecord/Associations/ClassMethods/has_many
When you use one-to-many associations you are telling your User model that he has zero or more instances of the model Advice, while Advice belongs to only one model User and its reference to it.
This is how your models should be:
class User < ApplicationRecord
has_many :advices
end
class Advice < ApplicationRecord
belongs_to :user
end
Even if you don't need to declare it right now, you might need it in a long run, so it is always better to have it set. You can read more about this topic here.

Rails acts_as_paranoid and has_many :through

So I'm using the rails3_acts_as_paranoid gem, and having some problems controlling scope with has_many :through associations.
For example
# User.rb
acts_as_paranoid
has_many :folders
has_many :files, :through => :folders
-
# Folder.rb
acts_as_paranoid
belongs_to :user
has_many :files, :dependent => :destroy
-
# File.rb
acts_as_paranoid
belongs_to :files
Now lets just say somewhere in the users_controller.rb i want to query all files belonging to a user, whether they are deleted, and/or belong to folders that have been deleted. So naturally I would assume to do something like the following
current_user.files.with_deleted
with_deleted method does it's job at removing the files.deleted_at IS NULL
...BUT... it doesnt remove the default_scope for folders which is used kind of behind the curtain. So we still have a folders.deleted_at IS NULL condition, preventing me from retrieving the files that belong to those folders where deleted_at is not null.
I want to keep using acts_as_paranoid, as it is incredibly useful in all other places of my app, and am trying not to do something like manual filtering and popping out elements of the .where_values array. But I don't know too much about handling complex scopes or what methods are available.
Well my question got down-voted, not sure why. But I found the answer:
When on a has_many through, the problem I was having was an inability to control the scope of the through model (Folders in this case).
Turns out you can just do this
#myvar = Folder.unscoped { current_user.files.with_deleted }
To whoever downvoted it - I'd like to know why, so I can ask better questions next time. Thanks!

Rails 3: How do I work with methods on a related object?

I'm updating a site with Rails 3.1 and adding some features. This application tracks tests for a collection of sites. Originally I was storing the grade for each test as a column in the Tests table but now I have created a new model called Reviews to keep a history of grades and comments assigned each test. The models look like this:
class Site < ActiveRecord::Base
has_many :tests
has_many :reviews, :through => :tests
class Test < ActiveRecord::Base
has_many :reviews
belongs_to :site
class Review < ActiveRecord::Base
belongs_to :test
has_one :user
Previously I could do something like this in the Site model to get some reporting data on grades:
def total_a_grades
tests.count(:conditions => "reviewer_grade = 4 and review_status = 3")
end
Since the grades are now out of the Test model I created this method to access the most recent grade for each test:
def last_grade
return reviews.find(:last).grade
end
This works fine for displaying the most recent grade in the test view, but how can I use it at the site level? Since the value is now in a method that's not stored in the database, I don't know how to work with it and can't find anything related to this at the major online tutorial/screencast sites.
Thanks in advance for your help!
The joins method allows access to the has_many :through association's attributes. Or start with the desired association, and the required joins are included for you.
tests.joins(:reviews).where("reviews.grade = 5").count
reviews.where("grade = 5").count
sources:
Active Record Query Interface at Rails Guides, section 11 Joining Tables
Railscasts 181: Include vs. Joins. Some of the methods described have changed; check out Railscasts 202.
Railscasts 215: Advanced Queries in Rails 3. Includes some examples of associations and joins.

Uninitialized constant error: Can't get this has_many :through correct

I have been around and around with this. Have seen similar questions here but it seems I have an extra complicating factor; what worked for them doesn't work for me.
I have models and tables for User, Group, GroupMember. A group is owned by a user, but each group can have an arbitrary number of group members, i.e., other users. Here are my associations:
In User,
has_many :groups
In Group,
belongs_to :user
has_many :group_members
has_many :members, :class_name => "User", :through=>:group_members
In GroupMember,
belongs_to :member, :class_name=>"User"
belongs_to :group
To get at the members of a group, then, in groups_controller.rb I do this:
#groupmembers = #group.group_members.all
However, that generates the following error:
NameError in GroupsController#show
uninitialized constant Group::GroupMember
Like I say, I have been around and around with this... where have I gone wrong? Thanks in advance for looking...
I finally got this working on my own. The part I was missing was in the User class; since User is the underlying class of Member, I needed this:
belongs_to :groupmember, :foreign_key=>"member_id"
Once that was in place, Rails was able to find everything as it should, e.g,
Group.find(1).members now finds all users who belong to the group with an ID of 1.
Assuming you have a model called GroupMembers (which you should given this is a has_many through association), your non-through association should look like this on both the Group and Member models:
has_many :group_members, :class_name => "GroupMembers"
For some reason rails isn't pluralizing the second model in the association, so just do it yourself.
Sometimes it can also be as simple as the belongs_to :model needs to be singular instead of plural. I made this mistake on my relationship today.

Filtering child objects in a has_many :through relationship in Rails 3

Greetings,
I have an application where Companies and Users need to belong to each other through a CompanyMembership model, which contains extra information about the membership (specifically, whether or not the User is an admin of the company, via a boolean value admin). A simple version of the code:
class CompanyMembership < ActiveRecord::Base
belongs_to :company
belongs_to :user
end
class Company < ActiveRecord::Base
has_many :company_memberships
has_many :users, :through => :company_memberships
end
class User < ActiveRecord::Base
has_many :company_memberships
has_many :companies, :through => :company_memberships
end
Of course, this makes it simple to get all the members of a company via company.users.all, et al. However, I am trying to get a list of all Users in a Company who are admins of that Company (and also to test whether a user is an admin of a given company). My first solution was the following in company.rb:
def admins
company_memberships.where(:admin => true).collect do |membership|
membership.user
end
end
def is_admin?(user)
admins.include? user
end
While this works, something feels inefficient about it (it's iterating over each membership, executing SQL each time, right? Or is Relation smarter than that?), and I'm not sure if there's a better way to go about this (perhaps using scopes or the fancy new Relation objects that Rails 3 uses?).
Any advice on the best way to procede (preferably using Rails 3 best practices) would be greatly appreciated!
I believe I was going about this the wrong way, specifying conditions on company_memberships instead of users, which was what I actually wanted (a list of Users, not a list of CompanyMemberships). The solution I think I was looking for is:
users.where(:company_memberships => {:admin => true})
which generates the following SQL (for company with ID of 1):
SELECT "users".* FROM "users"
INNER JOIN "company_memberships"
ON "users".id = "company_memberships".user_id
WHERE (("company_memberships".company_id = 1))
AND ("company_memberships"."admin" = 't')
I'm not sure yet if I'll need it, but the includes() method will perform eager loading to keep down the number of SQL queries if necessary:
Active Record lets you specify in
advance all the associations that are
going to be loaded. This is possible
by specifying the includes method of
the Model.find call. With includes,
Active Record ensures that all of the
specified associations are loaded
using the minimum possible number of
queries.queries. RoR Guides: ActiveRecord Querying
(I'm still open to any suggestions from anyone who thinks this isn't the best/most effective/right way to go about this.)
An even cleaner way would be to add an association to your Company model, something like this:
has_many :admins, :through => :company_memberships, :class_name => :user, :conditions => {:admin => true}
You might have to dig into the rails doc to get the exact syntax right.
You shouldn't need :include, unless you have other classes associated with :user that you might reference in your view.
How about something like this:
Company.find(:id).company_memberships.where(:admin => true).joins(:user)
I stumbled across this answer and believe that nowadays there is a nicer way using has_many association scopes (has_many documentation):
has_many :admins, -> { where(admin: true) }, through: :company_memberships, class_name: :user
The second parameter of a has_many association can be a proc or lambda that contains your filter.
I made the activerecord_where_assoc gem to do this.
With it, no need for joins or includes. Doing eager loading remains your choice, not an obligation.
users.where_assoc_exists(:company_memberships, admin: true, company_id: #company.id)
The question is unclear on how to filter the company, so I added it myself. Without this, if a user can be in multiple company, it being admin in one company could mean being treated as an admin in another company.
The gem doesn't work in Rails 3, but we are 9 years later and Rails 4.1 and more are supported.
Here are the introduction and examples. Read more details in the documentation.

Resources