I'm trying to manually add data to a table in rails using the rails console, but I keep getting a undefined local variable or method error.
The model is namespaced under ratings:
models/ratings/value_for_money_score.rb
class Ratings::ValueForMoneyScore < ApplicationRecord
end
To try and manually add a record to the database, I run the following in the console:
$ RatingsValueForMoneyScore.create(score: '1', description:"Terrible, definitely not worth what was paid!")
And I get this error: NameError: uninitialized constant RatingsValueForMoneyScore
I have tried a few different versions such as
RatingsValueForMoneyScores.create,
Ratings_ValueForMoneyScores.create,
ratings_value_for_money_scores.create but keep getting the same error. What am I doing wrong?
Try
::ValueForMoneyScore.create(score: '1', description:"Terrible, definitely not worth what was paid!")
The error is descriptive enough in this case, the class RatingsValueForMoneyScore pretty much doesn't exist. Check your namespace, you have a class name (which should be singular) Rating, and the module ValueForMoneyScore. You'd use either
(after renaming the class to Rating)
Rating.create(...)
or
ValueForMoneyScores.create(...)
Your syntax is equivalent to:
class Rating
module ValueForMoneyScores < ApplicationRecord
...
end
end
The answer was a combination of input, so collecting that in one answer.
My namespaces weren't properly set up (I was using "Ratings" rather than "Rating"), so I fixed this for the tables. My models then looked as follows:
models/rating.rb
class Rating < ApplicationRecord
end
models/rating/value_for_money_score.rb
class Rating::ValueForMoneyScore < ApplicationRecord
end
And then this command worked for creating records in the rating_value_for_money_score table
$ Rating::ValueForMoneyScore.create(score: '1', description: "Test")
Related
I am storing an array of typed objects in an ActiveRecord model like:
class Store::Bin < ActiveRecord::Base
serialize :items, Array
end
class Store::Item
include Virtus.model
attribute :name, String
...
end
When I make a code change in development mode and refresh my browser, I get an undefined class/module Store::Item exception.
It seems like something is getting crossed up with the class loading. All files are in the app/models/store/... directory, properly named w/r to their camelcase name.
The same issue happens when using the rails console. reload! does not fix the issue in the console; instead I have to exit and restart the console.
Adding a type to the array seemed to solve the problem.... but caused an issue with the associated FactoryGirl factory.
class Store::Bin < ActiveRecord::Base
serialize :items, Array[Store::Item]
end
UPDATE: the real issue was that when a code change is made to store/bin.rb, that class gets auto-loaded, but the auto loader had no idea that Store::Item was a dependency.
THE REAL FIX: Declare the required dependency using require_dependency
require_dependency "store/item"
class Store::Bin < ActiveRecord::Base
serialize :items, Array
end
You should avoid the :: operator when defining classes because of Rails autoload problems. Instead, try
module Store
class Item
# ...
end
end
When you're not sure what's going on you can use Module.nesting to figure out how Rails interprets the hierarchy.
I have an application I'm building where I need one model to create instances of another model. I want every Car to have 4 tires.
Car model
class Car < ActiveRecord::Base
has_many :tires
after_create :make_tires
def make_tires
4.times { Tire.create(car: self.id) }
end
end
Tire model
class Tire < ActiveRecord::Base
belongs_to :car
end
However, inside of make_tires there is an error that there is no activerecord method for create or new if I try it for Tire. When I inspect Tire it doesn't have those methods.
How can I remedy this?
The error is this: undefined method 'create' for ActiveRecord::AttributeMethods::Serialization::Tire::Module
I have tested two environments: Testing and Development and they both fail for the same error.
It is a name conflict. Sit down and relax while I explain.
Solution with explanation:
In Ruby classes are just instances of class Class (which is a subclass of class Module). Instances of Module (including instances of Class) are quite weird objects, especially weird is their connection with ruby constants. You can create a new class at any point using standard ruby notation:
my_class = Class.new { attr_accessor :a }
instance = my_class.new
instance.a = 3
insatnce.a #=>
instance.class.name #=> nil
Well, our class has no name. It is just an anonymous class. How do classes get their name? By assigning it to a constant (for the first time):
MyClass = my_class
my_class.name #=> 'MyClass'
When you define class using a class keyword:
class MyClass
...
end
You just create a new instance of Class and assign it to a constant. Because of that, Ruby compiler seeing a constant has no idea whether it is a class or a number under it - it has to make a full search for that constant.
The logic behind finding a constant is quite complex and depends on the current nesting. Your case is quite simple (as there is no nesting), so ruby will try to find Tire class inside your class first and when failed it's subclasses and included modules.
Your problem is that your class inherits from ActiveRecord::Base (which is correct), which includes ActiveRecord::AttributeMethods::Serialization module, which defines Tire constant already. Hence, ruby will use this constant instead, as this is the best match for that name in given context.
To fix it, you must tell the compiler not to look within the current class but directly in the "top namespace" (which in ruby is Object. Seriously, try Object.constants) - you can do that using :: in front of your constant, like ::Tire.
Note: even though it works, this issue is a first warning for you that your code starts to smell. You should look after this ActiveRecord::AttributeMethods::Serialization::Tire::Module thingy as it seems you will encounter it more than once in the future.
Other stuff:
You can simplify your method slightly:
def make_tires
4.times { tires.create }
end
At that point you might encounter some error you had initially. If you do, then please find what is going on with that Tire::Module thing. If you don't care about the smell:
has_many :tires, class_name: '::Tire'
I'm not sure what's causing the exception you are seeing but you have a number of issues. First, you need to pass in a car instance instead of the id in make_tires. Like this:
def make_tires
4.times { Tire.create(car: self) }
end
You also need to have attr_accessible :car in the Tire model. Like this:
class Tire < ActiveRecord::Base
belongs_to :car
attr_accessible :car
end
I'm trying to do a pretty simple join in my model to list all 'Locations' in a 'Post' with a certain id.
Currently, each post has_many :locations, :through => :location_post. I'm using the 'blogit' gem, which puts posts in a module named 'Blogit::Posts'.
I'm getting a wrong argument type Class (expected Module) error when I try to run the following in my Post.rb model:
module Blogit
class Post < ActiveRecord::Base
before_save :reuse_existing_locations
def reuse_existing_locations
existing_locations = Location.include(Blogit::Post).first
end
How can I do a join through a module?
Thanks in advance!
I'm not sure I understand what you're trying to accomplish so just some notes and observations:
By looking at the code, it's clear that Blogit::Post is a class, not a module.
The include method takes modules (not classes), that's the error you're seeing.
You are calling the include method on the Location model and that seems kind
of strange to me. Did you mean to call includes? But then again that
wouldn't make much sense since it seems like you've got a many to many
relationship between Location and Blogit::Post.
In the Location model (which doesn't need to be in the Blogit namespace), you can simply reference the Blogit::Post model as
follows:
has_many :posts, class_name: "Blogit::Post", ...
If existing_locations is in fact an attribute on the model and you want to assign to it, you need to put self in front of it (as in self.existing_locations). Otherwise you're just creating a local variable.
You probably wanted to use ActiveModels includes instead of Rubys include, which is to include methods from another module.
I have a model which uses STI:
class Contributor::Name < Contributor::NameBase
...
end
From this model:
class Contributor::NameBase < ActiveRecord::Base
...
end
Whenever Contributor::Name gets instantiated, I receive this error:
Mysql2::Error: Table 'shelflives_development.contributor_basis_name_bases' doesn't exist: SHOW FULL FIELDS FROM `contributor_basis_name_bases`
It seems that instead of looking up the table contributor_name_bases, ActiveRecord is looking up contributor_basis_name_bases. Why is adding basis between contributor and name_bases? How can I get it to stop?
Ok, it's not a answer about why rails is adding 'basis' but it will work for you.
Use set_table_name 'contributor_name_bases' in your model.
In my Rails I have the following models:
A STI sub-class
class Subscription::Discount < Subscription
def self.new_with_url
...
end
end
and another model class (doing completely different things, this is a STI base class)
class Discount < ActiveRecord::Base
end
So in my controller, I uses Subscription::Discount when I create users:
#user.subscription = ::Subscription::Discount.new_with_url()
However it complains: undefined method 'new_with_url' for #<Class:0x007fbb499c6740>
I think Rails is not calling the right class with new_with_url. On top of that I am not sure what #<Class:0x007fbb499c6740> is. So, two questions:
Without renaming any model, how can I reference Subscription::Discount properly?
Why is the error message saying #<Class:0x007fbb499c6740>, I can understand if it is Discount instead of that anonymous class.
EDIT:
Here are all the relevant models:
app/model/discount.rb
app/model/coffee_discount.rb (CoffeeDiscount < Discount)
app/model/subscription.rb
app/model/subscription/discount.rb (Subscription::Discount < Subscription)
The method is named create_with_url but you're calling new_with_url.
Fix the method name.