I'm making a system to generate pub quizzes for pubs. To ensure that a pub doesn't receive the same quiz twice, pubs and quizzes have a many to many association.
class Quiz < ActiveRecord::Base
has_and_belongs_to_many :pubs
serialize :rounds, Array
before_create :generate_rounds
def generate_rounds
# Round class initializes with array of pubs
NUMBER_OF_ROUNDS.times { rounds << Round.new(pubs: self.pubs) }
end
end
class Pub < ActiveRecord::Base
has_and_belongs_to_many :quizzes
end
A quiz has rounds (an array of Round objects that contain questions) that are serialized using the ActiveRecord method serialize.
When I run this code:
q = Quiz.new
q.pubs << Pub.create
q.save
I got:
ArgumentError: undefined class/module HABTM_Pubs
Previously, I had a belongs_to relation (a quiz belonged to one pub) and this error didn't occur.
When I comment out the before_create callback (so rounds don't get created), the q.save action succeeds.
From my schema.rb
create_table "pubs_quizzes", id: false, force: true do |t|
t.integer "pub_id"
t.integer "quiz_id"
end
Things like Quiz.new.pubs work.
Edit: the stack trace
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/class_loader.rb:53:in `path2class'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/class_loader.rb:53:in `resolve'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/class_loader.rb:45:in `find'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/class_loader.rb:27:in `load'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:360:in `resolve_class'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:87:in `deserialize'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:122:in `visit_Psych_Nodes_Scalar'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/visitor.rb:15:in `visit'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/visitor.rb:5:in `accept'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:31:in `accept'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:302:in `block in revive_hash'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:300:in `each'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:300:in `each_slice'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:300:in `revive_hash'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/to_ruby.rb:161:in `visit_Psych_Nodes_Mapping'
from /home/geert/.rvm/rubies/ruby-2.1.2/lib/ruby/2.1.0/psych/visitors/visitor.rb:15:in `visit'
It must have something to do with serialization.
Relevant item?
YAML::load raises undefined class/module error
Serializing an array with objects that reference an ActiveRecord association seems to cause the error.
replacing
Round.new(pubs: self.pubs)
with
Round.new(pub_ids: self.pubs.map { |pub| pub.id })
and fetching the pubs in the Round class itself makes the error go away.
Related
I have an example of code not passing in test but working in the console.
Failing Test:
describe ImporterProfile do
it 'sends defaults method to EventAttribute model' do
expect(ListPage).to receive(:new) #passes
expect(EventAttribute).to receive(:new) #fails
ImporterProfile.new.standard_profile
end
1) ImporterProfile standard_profile sends new method to associated objects
Failure/Error: importer_profile.standard_profile
NoMethodError:
undefined method `each' for nil:NilClass
# ./app/models/importer_profile.rb:51:in `standard_profile'
# ./spec/models/importer_profile_spec.rb:29:in `block (3 levels) in <top (required)>'
Models:
class ImporterProfile < ActiveRecord::Base
has_one :list_page, dependent: :delete
has_many :event_attributes, dependent: :delete_all
accepts_nested_attributes_for :list_page
accepts_nested_attributes_for :event_attributes
def standard_profile
self.list_page = ListPage.new
self.event_attributes = EventAttribute.new
end
end
class EventAttribute < ActiveRecord::Base
belongs_to :importer_profile
end
class ListPage < ActiveRecord::Base
belongs_to :importer_profile
end
However, running this method in the console instantiates a new ImporterProfile, ListPage and several EventAttribute objects.
Anyone understand what is going on here?
I suspect that the problem is that you are mocking EventAttribute.new, but only returning nil, so Rails can't enumerate the active records as is required by the self.event_attributes = statement. (It needs to set the foreign key attribute of the EventAttribute records to the id of the ImporterProfile record.)
If you don't mind continuing with execution, you can do:
expect(EventAttribute).to receive(:new).and_call_original
Alternatively, you can return a double, but you'll need to provide stubs for whatever methods ActiveRecord requires, either by using a library such as http://rubygems.org/gems/rspec-active_record_mocks/versions/0.1.4 or by rolling your own.
As an aside, this question would have been a little easier to answer if you'd provided some way to associate the line numbers in the error stack trace with the sources you provided. Also, the comments on your expect statements that the first passes and the second fails is confusing because it appears that you are raising an error before the expectations are being checked.
I have next Siteable concern:
module Siteable
extend ActiveSupport::Concern
included do
belongs_to :site
scope :by_site, lambda { |site| where(site_id: site.try(:id)) }
scope :for_site, lambda { |site| by_site self.class.by_site(site).any? ? site : nil }
end
end
Example model with this concern:
class IndustryLink < LinkResource
require 'concerns/siteable.rb'
include Siteable
belongs_to :author, :class_name => 'User'
belongs_to :industry
validates_presence_of :name, :link
end
It work fine on the server. But all specs with this model fails with similar errors:
Failure/Error: industry = Factory(:industry, :name => 'new industry')
NoMethodError:
undefined method `by_site' for Class:Class
# ./app/models/concerns/siteable.rb:8:in `block (2 levels) in <module:Siteable>'
# ./app/models/industry_link.rb:12:in `get_objects'
# ./app/models/concerns/reorderable.rb:47:in `add_number'
# ./spec/views/sitemap/show.xml.builder_spec.rb:41:in `block (2 levels) in <top (required)>'
So, obviously that self.class is not Industry in this case and I don't know how to fix this.
If I move for_site to model and change self.class to Industry specs passes.
Checked for ruby 1.9.3, 2.1.1, Rails 3.2.19
Within your lambda self is already the model class - Industry, so the self.class is Class
You should just be using by_site (possibly unscoping it first, depending on what you need)
I wan't to test a has_many association on a class:
class Course < ActiveRecord::Base
has_many :modules
end
For this I wrote a test (Rspec):
describe Course do
it { should have_many(:modules) }
end
For some reason however this test fails:
1) Course should have many modules
Failure/Error: it { should have_many(:modules) }
NoMethodError:
undefined method `column_names' for Module:Class
# ./spec/models/course_spec.rb:4:in `block (2 levels) in <top (required)>'
Does someone has an idea why this test fails? I created a Module class:
class Module > ActiveRecord::Base
belongs_to :course
end
Could it be that 'Module' is a reserved keyword, and therefore I cannot create a class Module?
Thanks for your help,
Anthony
Module is "reserved" name in Ruby (since Ruby has build-in - and very important - Module class). This is probably source of your error.
I am learning Rails with the book Agile Web Development with Rails 4th edition.
Given the code for the migration below:
class CombineItemsInCart < ActiveRecord::Migration
def up
Cart.all.each do |cart|
sums = cart.line_items.group(:product_id).sum(:quantity)
sums.each do |product_id, quantity|
if quantity > 1
cart.line_items.where(product_id: product_id).delete_all
cart.line_items.create(product_id: product_id, quantity: quantity)
end
end
end
end
def down
LineItem.where("quantity>1").each do |line_item|
line_item.quantity.times do
LineItem.create(cart_id: line_item.cart_id, product_id: line_item.product_id, quantity: 1)
end
line_item.destroy
end
end
end
The following error occurs:
== CombineItemsInCart: migrating =============================================
rake aborted!
An error has occurred, this and all later migrations canceled:
Can't mass-assign protected attributes: quantity/home/richard/projects/pickaxe/mini-projects/depot-app/db/migrate/20130607003533_combine_items_in_cart.rb:9:in `block (2 levels) in up'
/home/richard/projects/pickaxe/mini-projects/depot-app/db/migrate/20130607003533_combine_items_in_cart.rb:6:in `each'
/home/richard/projects/pickaxe/mini-projects/depot-app/db/migrate/20130607003533_combine_items_in_cart.rb:6:in `block in up'
/home/richard/projects/pickaxe/mini-projects/depot-app/db/migrate/20130607003533_combine_items_in_cart.rb:3:in `each'
/home/richard/projects/pickaxe/mini-projects/depot-app/db/migrate/20130607003533_combine_items_in_cart.rb:3:in `up'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
Now I read somewhere that this book was written before attr_accessible was required by default, but it hasnt really touched on how to use it properly yet. I have tried adding :line_item or :line_items to my attr_accessible line in the Cart model, but no luck.
Could someone educate me on what is happening here?
Can't mass-assign protected attributes: quantity
try attr_accessible :quantity
you will need to list all of the attributes in that list.
You need to make the attributes accessible. In the model:
class Object ActiveRecord::Base
attrib_accessible :attrib1, :attrib2, :attrib3
end
Obviously you would replace the attrib1 etc. with your model's attributes.
You try to access mass attributes from your migration. When you want to access database table attributes from your code then you need to allowing mass assignment of that attributes to tell your model that you can able to assign data for the field by the code. For this pusrpose, just add the desired field as a attr_accessible and for your issue specific solution is bellow :
class LineItem < ActiveRecord::Base
attr_accessible :quantity, :product_id, :cart_id
end
In my rails application, i'm unable to save the model object when associations are present.
I'm using mongo as db.
Brief explanation:
I have a model object,
#obj1 = User.create(name: "name1")
When i do #obj1.save, it works fine. Now i added a relationship say,
has_many :offices
and then i try to save the same object with new entry.
#obj1 = User.create(name: "name2")
I get a error like
/gems/activesupport-3.2.9/lib/active_support/inflector/methods.rb:230:in `block in constantize'
gems/activesupport-3.2.9/lib/active_support/inflector/methods.rb:229:in `each'
Edit:
Full error trace:
NameError: uninitialized constant Office
from /home/workspace/.rvm/gems/ruby-1.9.3-p286#cv_app/gems/activesupport-3.2.9/lib/active_support/inflector/methods.rb:230:in `block in constantize'
from /home/workspace/.rvm/gems/ruby-1.9.3-p286#cv_app/gems/activesupport-3.2.9/lib/active_support/inflector/methods.rb:229:in `each'
from /home/workspace/.rvm/gems/ruby-1.9.3-p286#cv_app/gems/activesupport-3.2.9/lib/active_support/inflector/methods.rb:229:in `constantize'
from /home/workspace/.rvm/gems/ruby-1.9.3-p286#cv_app/gems/activesupport-3.2.9/lib/active_support/core_ext/string/inflections.rb:54:in `constantize'
In your user model
class User < ActiveRecord::Base
has_many :offices
end
In your office model
class Office < ActiveRecord::Base
belongs_to :user
end
Now, try this
#user = User.new
#user.name = "xyz"
#user.save
for relationship
#user = User.offices.build
Check whether the office model and table has created or not. It these two were created it won't give this kind of error.