RoR: Instance Variable Interpreted as Missing Method - ruby-on-rails

I'm following a tutorial and per the code, I should be able to call the following wihtout error:
page = Page.find(1) # works
page.sections.size # Does not work
subject = Subject.find(1) # works
subject.pages.size # works
A section belongs_to a page, and a page belongs_to a subject. I'm trying to count the number of sections that are associated with the respective page (in this case, page :id => 1).
The error is Undefined Method but I'm not accessing a method, I'm accessing an instance variable. I've reviewed my models and controller, and there is no scope or declerations defined for subject.pages.size yet it works without complaint. I'm quite perplexed why it's not working for it's child, page.sections when I'm trying to do the same operation.
The diagnostic info (from CLI using pry) can be found here: http://pastebin.com/xKKvSPkz
DB Schema: http://pastebin.com/hiAhXGt8

Ensure that the relationship between page and section is defined:
class Page < ActiveRecord::Base
has_many :sections
end
class Section < ActiveRecord::Base
belongs_to :page
end
With this relationship the following should work as expected:
page = Page.find(1)
page.sections.size

Related

Rails ActiveAdmin has_one and belongs_to causes 'Undefined Method for <PLURAL_RESOURCE>'

In Rails, I have a 'User' model and a 'Wallet' model. A 'User' has_one wallet and each 'Wallet' belongs_to a 'User'. I made a 'Show' page in ActiveAdmin to view a User's Wallet. However, going to that page returns this error:
undefined method `wallets' for #<User:0x007f...>
HOWEVER, when I update the User model to 'has_many :wallets' instead of ':has_one wallet', everything works. Here is the relevant code from my models and ActiveAdmin code:
Models:
class User < ActiveRecord::Base
has_one :wallet, dependent: :destroy
end
class Wallet < ActiveRecord::Base
belongs_to :user
end
ActiveAdmin:
ActiveAdmin.register Wallet do
belongs_to :user
actions :all, except: :destroy
show do
div do
'hello'
end
end
end
ActiveAdmin.register User do
actions :all, except: :destroy
permit_params
action_item :wallet, only: :show do
link_to('Wallet', admin_user_wallet_path(user, user.wallet.id))
end
index do...
end
Any ideas as to where I might have gone wrong?
Edit 1: updates to correct colon placement mistakes in description
Edit 2:
in response to:
Can you show your routes file? Also, can you give us the full traceback of the error message and give us the output of rake routes? I suspect that the reason it's complaining about wallets not being defined (even though you never call wallets in the above code) is that some routing is making assumptions about how the relationships look. – Glyoko 4 mins ago
My routes file contains no mention 'wallet' or 'wallets'.
My stack error more specifically looks like this:
activemodel (4.1.15) lib/active_model/attribute_methods.rb, line 435
Let me know if you need more than that.
Here's the related output from 'bin/rake routes':
admin_user_wallets GET /admin/users/:user_id/wallets(.:format) admin/wallets#index
POST /admin/users/:user_id/wallets(.:format) admin/wallets#create
new_admin_user_wallet GET /admin/users/:user_id/wallets/new(.:format) admin/wallets#new
edit_admin_user_wallet GET /admin/users/:user_id/wallets/:id/edit(.:format) admin/wallets#edit
admin_user_wallet GET
/admin/users/:user_id/wallets/:id(.:format) admin/wallets#show
admin_user_wallet PATCH /admin/users/:user_id/wallets/:id(.:format) admin/wallets#update
admin_user_wallet PUT /admin/users/:user_id/wallets/:id(.:format) admin/wallets#update
ActiveAdmin uses InheritedResources gem internally, the belongs_to method ends up inside InheritedResources.
Possible solution here
ActiveAdmin.register Wallet do
belongs_to :user, singleton: true
actions :all, except: :destroy
end
The option singleton: true makes the Wallet a singular resource for the User.
Probably, another option optional: true may be helpful if Wallet is not required for any User to present
Even though your routes may not explicitly reference wallet(s), there may be something there making assumptions about how records are related to each other.
Look at the output of rake routes, in particular:
admin_user_wallet GET
/admin/users/:user_id/wallets/:id(.:format) admin/wallets#show
When you call admin_user_wallet_path(user, user.wallet.id) it's matching the /admin/users/:user_id/wallets/:id(.:format) route. Notice how that expects both a user id and a wallet id in the path. This is a tip-off that something is off here, since if you have the user, there should be exactly one wallet associated with it. You shouldn't need to give both the user and wallet id.
Since the wallet resource is nested under users, the page where you view the user's wallet is actually more of an index page than a show. If the wallet were an independent resource, then you could have a path like /admin/wallets/:id and things would work out fine.
But since the wallet is a subresource of the user, you would ideally want a path like /admin/users/:user_id/wallet. There's no need to pass the wallet id, since you already have the user.
tl;dr: Try changing the shows to indexs and see where that gets you. e.g.
index do
div do
'hello'
end
end
# ...
action_item :wallet, only: :index do
link_to('Wallet', admin_user_wallets_path(user))
end
Okay.. So I had this same exact issue. I had a belongs_to a parent where the parent had only a has_one to the child model..... Nothing seemed to work so I decided to fake it. I am not sure if this is the best way to do this but it worked. In the parent model, add a method:
class User < ActiveRecord::Base
has_one :wallet
def wallets
Wallet.where(user_id: id)
end
end
The above code is a hotfix until I can find some other way to implement what I need.

Undefined method error for scope on STI subclass

The Setup
I have an STI setup like so:
class Transaction < ActiveRecord::Base
belongs_to :account
scope :deposits, -> { where type: Deposit }
end
class Deposit < Transaction
scope :pending, -> { where state: :pending }
end
class Account < ActiveRecord::Base
has_many :transactions
end
If I call:
> a = Account.first
> a.transactions.deposits
...then I get what I expect, a collection of Deposit instances, however if I look at the class of what's returned:
> a.transactions.deposits.class
...then it's actually not a Deposit collection, it's still a Transaction collection, ie. it's a Transaction::ActiveRecord_AssociationRelation
The Problem
So, to the problem, if I then want to call one of the Deposit scopes on that collection it fails:
> a.transactions.deposits.pending
NoMethodError: undefined method `pending' for #<Transaction::ActiveRecord_Associations_CollectionProxy:0x007f8ac1252d00>
Things I've Checked
I've tried changing the scope to Deposit.where... which had no effect, and also to Deposit.unscoped.where... which actually returns the right collection object, but it strips all the scope, so I lose the account_id=123 part of the query so it fails on that side.
I've checked this and the problem exists for both Rails 4.1 and 4.2. Thanks for any pointers on how to make this work.
I know there's a workaround, but...
I know I could work around the issue by adding a has_many :deposits into Account, but I'm trying to avoid that (in reality I have many associated tables and many different transaction subclasses, and I'm trying to avoid adding the dozens of extra associations that would require).
Question
How can I get what's returned by the deposits scope to actually be a Deposit::ActiveRecord_Association... so that I can chain my scopes from Deposit class?
I created an isolated test for your issue here:https://gist.github.com/aalvarado/4ce836699d0ffb8b3782#file-sti_scope-rb and it has the error you mentioned.
I came across this post from pivotal http://pivotallabs.com/merging-scopes-with-sti-models/ about using were_values in a scope to get all the conditions. I then used them on unscope to force the expected class, basically this:
def self.deposits
conditions = where(nil).where_values.reduce(&:and)
Deposit.unscoped.where(conditions)
end
This test asserts that it returns a Deposit::ActiveRecord_Relation https://gist.github.com/aalvarado/4ce836699d0ffb8b3782#file-sti_scope2-rb
Update
You can also write this as a scope if you prefer:
scope :deposits, -> { Deposit.unscoped.where where(nil).where_values.reduce &:and }
As a quick workaround you can do > a.transactions.deposits.merge(Deposit.pending), but can't think of a different way of solving it. I'll think and try more options later and come back if I find anything.
You might want to say that an Account has_many :deposits
class Account < ActiveRecord::Base
has_many :transactions
has_many :deposits
end
Then you should be able to query
a.deposits.pending

self join association undefined method for nil:NilClass

I have a model, Item, that comes in three types, implemented using Single Table Inheritance. Item has a tree hierarchy that is represented using a has_many :through relationship for both the parents (called groups) and the children (called sub_items). One of the subclasses (we'll call it ItemA) must have only one parent at all times (but the other subclasses can have 0 or more parents). I couldn't figure out how to implement validations that would enforce the rules of the tree hierarchy, so I left them out.
ItemA has a helper method, parent, to get its parent. Sometimes this method will raise undefined method 'first' for nil:NilClass. It doesn't always happen, but it does in certain situations.
Situation 1
I am using jqGrid to list all the ItemAs. I use sorting functionality for some of the columns, including the parent. The grid would initially load the ItemAs successfully, showing that they all had parents as they should. However, when I tried to sort the parent column, I would get the error as if they suddenly disappeared. It went away when I removed includes(:groups) from the sorting method. I don't understand why that would help, but I figured the problem was solved. Until...
Situation 2
I am testing my app with Rspec, Factory Girl, and Selenium. In one of my tests, I call the parent method on an instance of ItemA created using Factory Girl. It raises the error. My tests that use the parent method indirectly don't fail. For example, the parent method is called on the index page for ItemA, and many tests visit that page without problem. This situation has not been resolved. My app no longer has any calls to includes, so that isn't part of it this time.
Relevant Code
ItemA
class ItemA < Item
# This method is called in Situation 1
def self.sort_by_parent sort_order
all.sort_by(&:parent_name).tap do |i|
i.reverse! if sort_order == :desc
end
end
def parent
groups.take
end
def parent_name
parent.component_name
end
end
Item
class Item < ActiveRecord::Base
has_many :item_groups, foreign_key: 'sub_item_id', dependent: :destroy
has_many :groups, through: :item_groups
has_many :group_items, class_name: 'ItemGroup', foreign_key: 'group_id', dependent: :destroy
has_many :sub_items, through: :group_items
end
update_spec
feature 'ItemA editing', js: true do
given!(:item_a) { create(:item_a) }
given!(:parent) { create(:item_b) }
scenario 'when parent', focus: true do
itemas_page = ItemAsPage.visit # This is a custom page object
# Situation 2 occurs here
itemas_page.edit_parent item_a.parent_name, parent.component_name
expect(itemas_page).to have_item_a_with parent.component_name
end
end
Why is the parent method sometimes reading as nil, and how do I get it to always produce a value?
EDIT: As I'm changing my code, I'm coming across more situations that cause this error. I checked the source. Here's a snapshot of ActiveRecord::FinderMethods:
module ActiveRecord::FinderMethods
def take(limit = nil)
limit ? limit(limit).to_a : find_take
end
private
def find_take
if loaded?
#records.first
else
#take ||= limit(1).to_a.first
end
end
end
For debugging purposes, I modified the parent method to look like this:
def parent
groups.tap {|g| puts '#records: ' + g.instance_variable_get(:#records).inspect }.take
end
#records is nil. I tried changing it to groups.reload.take to get #records to load, but it's not working. I'm using groups.limit(1).to_a.first right now, which is working, but I'm curious to find out what sort of bug in my app is causing this problem.
It turns out that despite calling reload, Rails still uses the cached version of the query. The same problem is described here. This fixes it:
class ItemA < Item
def parent
ActiveRecord::Base.uncached { groups.take }
end
end

set_table_name doesn't work if model is in a deep namespace - bug in rails 2.3.14?

I'm using Rails 2.3.14
UPDATE 3 the trick I found in update 2 only works for the first access of the association... so, I stick the set_table_name lines in my application controller. This is so weird. I hope this gets fixed. =\
UPDATE 2 if I manually / nastily / hackily set teh tables for the troublesome classes at the bottom of my environment file, I stop getting the errors.
app/config/environment.rb:
Page::Template::Content.set_table_name "template_page_contents"
Page::Template::Section.set_table_name "template_page_sections"
Why do I have to do this? is rails broken for this particular use case?
UPDATE: it appears that set_table_name isn't working when it's initially called in my Page::Template::Content class.
but if I call it right before I use the association, it works...
ap Page::Template::Content.table_name # prints "contents"
Page::Template::Content.set_table_name "template_page_contents"
ap Page::Template::Content.table_name # prints "template_page_contents"
return self.page_template_contents.first # doesn't error after the above is exec'd
ORIGINAL QUESTION:
TL; DR: I have a has_many relationship that I'm trying to access, but Rails thinks that the table that the has_many relationship uses is a different table
I've been getting this error, where when I try to access a Page::Template::Content that belongs_to a Page::Template via a has_many relationship.
Mysql2::Error: Unknown column 'contents.template_page_id' in 'where clause': SELECT * FROM `contents` WHERE (`contents`.template_page_id = 17) LIMIT 1
Looking at the error logs, I figured I needed to starting using some print statements to find out why rails was trying to find the associated objects in the wrong table.
gems_path/activerecord-2.3.14/lib/active_record/associations/association_collection.rb:63:in `find'
I just decided to print the #reflection object since everything seems to be happening around that. Here is how I did that:
require "awesome_print" # best console printer (colors, pretty print, etc)
def find(*args) # preexisting method header
ap "-------" # separator so I know when one reflection begins / ends, etc
ap #reflection # object in question
... # rest of the method
Last '#reflection' that printed before the error:
"-------"
#<ActiveRecord::Reflection::AssociationReflection:0x108d028a8
#collection = true,
attr_reader :active_record = class Page::Template < LibraryItem {...},
attr_reader :class_name = "Page::Template::Content",
attr_reader :klass = class Content < LibraryItem {...},
attr_reader :macro = :has_many,
attr_reader :name = :page_template_contents,
attr_reader :options = {
:foreign_key => "template_page_id",
:class_name => "Page::Template::Content",
:extend => []
},
attr_reader :primary_key_name = "template_page_id",
attr_reader :quoted_table_name = "`contents`"
>
there are a couple things wrong in the above block of code.
:klass should be Page::Template::Content
:name should be :contents
:quoted_table_name should be `contents`
How my models are set up:
app/models/page.rb:
class Page < LibrayItem
belongs_to :template_page, :class_name => "Page::Template"
app/models/page/template.rb
class Page::Template < Library Item
set_table_name "template_pages"
has_many :page_template_contents,
:class_name => "Page::Template::Content",
:foreign_key => "template_page_id"
app/models/page/template/content.rb
class Page::Template::Content
set_table_name "template_page_contents"
belongs_to :template_page,
:class_name => "Page::Template",
:foreign_key => "template_page_id"
class Page::Template
...
return self.page_template_contents.first
the class the association is choosing (unrelated to my page / template structure above):
class Content < LibraryItem
set_table_name "contents"
# no associations to above classes
So... what is causing this and how do I fix it?
Your issue is not due to set_table_name but rather due to how Rails finds the target model class in an association and the fact that you have a top-level model (Content) which shares its name with a model nested in a deeper namespace (Page::Template::Content). The weird difference in behaviour is most likely due to differences in what classes are actually loaded at the time the association is examined (remember that by default in development mode Rails loads model classes on demand when they are first referenced).
When you define an association (whether you specify the class name of the target model of the association explicitly as you have done or accept the default), Rails has to turn the name of the target model class into a Ruby constant so that it can refer to that target class. To do this, it invokes the protected class method compute_type on the model class that is defining the association.
For example, you have
class Page::Template < LibraryItem
set_table_name "template_pages"
has_many :page_template_contents,
:class_name => "Page::Template::Content",
:foreign_key => "template_page_id"
end
You can test in the Rails console how compute_type works for this class:
$ ./script/console
Loading development environment (Rails 2.3.14)
> Page::Template.send(:compute_type, "Page::Template::Content")
=> Page::Template::Content(id: integer, template_page_id: integer)
I see the result is, as I expect, a reference to the class Page::Template::Content.
However, if I restart the Rails console but this time cause the model class Content to be loaded first (by referencing it) and then try again, I see this (I didn't bother creating a table for the Content model, but that doesn't change the significant behavior):
$ ./script/console
Loading development environment (Rails 2.3.14)
>> Content # Reference Content class so that it gets loaded
=> Content(Table doesn't exist)
>> Page::Template.send(:compute_type, "Page::Template::Content")
=> Content(Table doesn't exist)
As you can see, this time I get a reference to Content instead.
So what can you do about this?
Well, first of all, you should realize that using namespaced model classes in Rails (certainly in 2.3) is a real pain. It's nice to organize your model classes in a hierarchy but it certainly does make life more difficult. As you can see above, if you have a class in one namespace that has the same name as a class in another namespace, this gets more hairy.
If you still want to live with this situation, I can make a couple of suggestions. One of the following may help:
Turn on class caching in development mode. This will cause all model classes to be preloaded rather than loading them on demand as is the default. It will make your code above more predictable, but may make development somewhat unpleasant (since classes will no longer be reloaded with each request).
Explicity require dependent classes in classes with problematic associations. For example:
class Page::Template < LibraryItem
require 'page/template/content'
set_table_name "template_pages"
has_many :page_template_contents, ...
end
Override the compute_type method in your classes where you know referencing associated class may go awry so that it returns the namespaced class no matter what. Without knowing your class hierachy in more detail I cannot give a full suggestion on how to do this, but a quick and dirty example follows:
class Page::Template < LibraryItem
set_table_name "template_pages"
has_many :page_template_contents,
:class_name => "Page::Template::Content",
:foreign_key => "template_page_id"
def self.compute_type(type_name)
return Page::Template::Content if type_name == "Page::Template::Content"
super
end
end

Rails 3: Reload partial with second level association

My Application structure is as follows:
class Study < ActiveRecord::Base
has_many :topics
class Topic < ActiveRecord::Base
has_many :references
I need to access the references on the show page of my Study model (study.topic.references). However, I need to retrieve references based on (say) radio button selection of topics. So when the user clicks on a topic, I need to retrieve that particular topic's references via AJAX.
Therefore, I wrote the following method in my Study model:
class Study < ActiveRecord::Base
def self.topic_references(topic_id)
Reference.where(:topic_id => topic_id)
end
I access the above method in my Study controller Show as follows:
class StudiesController < ApplicationController
#references= Study.topic_references(params[:topic_id])
And I plan to have an remote form that will access the above method and retrieve the corresponding references. Something like this:
- form_tag topic_references, :id=>"references_form", :method => 'get' do
= text_field_tag :topic_id, params[:topic_id], :value=>268
= submit_tag "Get references"
As an initial test I am passing the value of topic ID (268 value above) however, the #references when accessed in the view is still empty. Kindly help me in understand what am I doing wrong and how can I achieve this?
You haven't shown your view code, but I am guessing that it is because where is returning a collection of objects rather than a single object, as discussed here:
.where vs find. ActiveRecord::Relation NoMethodError
In the console, it's easy to miss the brackets in output like [#<Reference id...] while debugging these kinds of problems.

Resources