Rails: Conflict Between Namespaced and Non-Namespaced Resources - ruby-on-rails

I have two Review models. The first is namespaced as Membership::Review and the second is not namespaced, Review. When I make a call on the non-namespaced model, Rails tries to lookup the namespaced one, instead. For instance:
library.includes(:reviews)
Gets me this error:
Expected C:/sites/shelflives/app/models/membership/review.rb to define Review
In my Library model, the Review association is made properly:
has_one :review
Even if I explicitly specify the model, I get the same error:
has_one :review, :class_name => "Review"
Any idea what's going on?

This kind of errors occurs when you adding extra config.autoload_paths in the application.rb with sub-folders of models.
You don't need to do this. All models will load automaticaly through the namespaces. You just need to organize correct structure with sub-folders of namespaces.
If you using namespaces with models you can use generator like this:
rails g model membership/review
That will generate correct namespaced model and will save it to membership sub-folder .

Related

Rails 3 joins table and model naming conventions

I am running Ruby 2.1.9 and Rails 3.2.17.
First off, in Rails I always made the assumption that models should almost always be singular. I have seen model names like product_categories_product. This is fine and in habtm situation where you want a model to work with the relationship I have seen instructions_products. Which in Rails sorta may make sense, but I would rather name the model instruction_product. That was associated with a joins table named instructions_products.
In fact I was so frustrated I started looking at how rails names things and this happened? So clearly its an ok name for the model and would correspond to the correct table name. But what is more approriate?
ActiveModel::Naming.singular(InstructionsProducts)
returns instructions_products
Edited: The heart of the question is why is InstructionsProducts a valid model name in rails and why does my example above resolve to the singular 'instructions_products'. This seems odd considering the rails convention is to keep model names singular.
Your question is not completely clear to me.
By Rails conventions, model names are singular and written in camel case, for instance InstructionProduct. Each model matches a table in the database with the same words, down-cased, separated by '_' and in plural. instruction_products for the provided example.
Look at the following example using has_many:
class User < ActiveRecord::Base
has_many :contacts
end
class Contact < ActiveRecord::Base
belong_to :name
end
user = User.find(1)
user.contacts # returns an array of all the associated objects
When doing user.contacts, contacts is not the table name. It's the collection method, a placeholder for the symbol passed in the has_many method (please follow the has_many link and read what documentation says about has_many). Another name could be used, though:
class User < ActiveRecord::Base
has_many :personal_contacts, class_name: 'Contact' #, foreign_key: :contact_id
end
user = User.find(1)
user.personal_contacts
The class_name and foreign_key are required because rails conventions are not being followed. When using has_many :personal_contacts rails expects that personal_contacts will return an array of PersonalContact.
In Ruby you must start a class name with a capital word, so it is not possible to create a class named instruction_product. If you want to provide a name that does not follow the Rails convention, which I don't recommend, you will need to inform rails about the new table name:
Class AdminUser
self.table_name = "users"
end
Update 1:
As you already know, the convention states that the model should be declared as singular (class InstructionProduct instead class InstructionsProducts. However its just a convention. When a class inherits from ActiveRecord::Base, and a sql query is generated, ActiveRecord lowercases the class name, separates the words by _, converts to a plural name and uses it as the table name (mainly rails uses InstructionsProducts.model_name.plural which returns instructions_products).
You are assuming that singular actually does a name translation to singular, even if it's written in plural, but it doesn't. It assumes that you are using the convention, and mainly returns the class name underscored.
Looking at the rails source code (ActiveModel::Name), ActiveSupport::Inflector.underscore seems to be used (I just did a very superficial investigation, I have to admit). You can see how underscore works at documentation.

Rails Joins through a module

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.

Devise not working due to this warning: [WARNING] You provided devise_for :users but there is no model User defined in your application

I've just added a fresh install of devise to a rails app but it's outputtin a warning on start server start and devise is not working at all:
[WARNING] You provided devise_for :users but there is no model User defined in your application
I 100% do have a model called users.
I've had a look about at others with this issue there is this one (Heroku [WARNING] You provided devise_for :users but there is no model User defined in your application) and I've changed the name of ':database_authenticable'. This did not fix it.
Also the devise initializer is set to active record, which is another bug which has this error (require 'devise/orm/active_record'), however this did not fix it either.
Has anyone else has this error, or is there a way I can trace it to get more details? Thanks.
I think if you have devise_for :users in your routes it will target your user model (singular). You said the name of your model is users, which could make it not work.
Check the model is in app/models/user.rb, not users.rb, and run ruby -c app/models/user.rb to do a syntax check.
If User exists and doesn't have a syntax error, it may be failing to load because it's trying to include something that doesn't exist. Comment out the body of the class and see if Devise lets you run rails console now.
rails generate devise User
Run this above code in Terminal, i mean in console so then User model will be generated for you.
Model Naming Convention
Table: users
Class: User
File: /app/models/user.rb
Primary Key: id
Foreign Key: user_id
Controller Naming Convention
Class: UsersController
File: /app/controllers/users_controller.rb
Layout: /app/layouts/users.html.erb
View Naming Convention
Helper: /app/helpers/users_helper.rb
Helper Module: UsersHelper
Views: /app/views/users/… (list.html.erb for example)
Tests Naming Convention
Unit: /test/unit/user_test.rb
Functional: /test/functional/users_controller_test.rb
Fixtures: /test/fixtures/users.yml
Rule: Model
The model is named using the class naming convention of unbroken MixedCase and is always the singular of the table name, e.g. table name might be users, the model name would be User. Rails will then look for the class definition in a file called user.rb in the /app/models directory. If the model class name has multiple capitalised words, the table name is assumed to have underscores between these words.
Note:
so if we say devise_for: users,
we must have a model with name User(user.rb)

ActiveRecord model inheritence and polymorphism

I have a class heirarchy but not single-table inheritence.
ThrowAway < ActiveRecord::Base
and
Junk < ThrowAway
the problem is that all ThrowAway objects have references to a Location. A Location
belongs_to :throw_away, :polymprohic => true
The problem is that if I define
ThrowAway < ActiveRecord::Base
has_many :locations, :as => :throw_away
end
then even if Junk inherits from it and defines a different table name, the throw_away_type column will always be set to ThrowAway where I actually want it set to Junk.
Now there will be many of these subclasses so there will be Stuff < ThrowAway Rags < ThrowAway etc. I want them all to have a Location without defining a location relationship in each individual class.
Is this possible with rails? Problem is that there is not just location, there are other of these sort of relationships and I'd rather follow some DRY here. I'm assuming I need to create a generator method which will execute on the current object and generate these relationships generators on runtime.
Thanks.
ActiveRecord doesn't seem to be able to cooperate with model inheritance that's not STI. Fortunately, that's probably not what you really want anyway. You probably want a mixin instead.
Create a module that contains all of the functionality that you want your models to share in common, and have all of your models include that module. I would probably put the module in your models directory, but give it an adjective as a name. Some folks might put the module in the lib directory or create a lib directory within the models directory for it.
In your case, you'll want the association to be defined at the time the module is mixed in, so you'll need to use a callback. Something like..
module Trashable
def self.included(base)
base.send :has_many, :locations, :as => :throw_away
end
end

How to access and submit related polymorphic models in the same form, in Rails?

Suppose I have 3 models, Car, Motorcycle and Truck, and for each I have to enter a bunch of stuff, such as a list of known previous owners, traffic tickets, license plates, etc. So I created a model for each (PreviousOwners, PreviousPlates, etc) and set up polymorphic associations for the related models.
The problem is, how can I enter all of that using just one form, kind of like this:
Car #123
Known previous owners:
Jason Jazz
Brian Bass [add another]
Known previous license plates:
12345
67890 [add another]
Current status:
Cleared
(this is a dropdown select menu, CurrentStatus is also a polymorphic association, but with predefined values.)
etc
This is proving to be a bitch, way beyond my level of expertise (newbie here). The resources are not nested and almost everything I find on multiple models is for nested resources, and nothing seems to apply to polymorphic associations.
(This is just an example, I know ideally I should have a Vehicle model with 'Car', etc, as categories, but it's just to illustrate the real need for polymorphic models in my case.)
Thanks.
Maybe the PresenterPattern is helpfull too:
http://blog.jayfields.com/2007/03/rails-presenter-pattern.html
The basic idea is to create a presenter which acts like a model and processes all the incoming data from your form and distributes it to the models. This way it's also easy to create multiple instances of lets say PreviousOwner and attach it to Car.
Check the link out!
You can use the new nested attributes in Rails 2.3, but there is a certain way you have to write it to make it work. The trick is that you need to create the actual polymorphic object, then build the class that has the belongs to polymorphic clause in it. This is an example I found at Ryans Scraps, posted by a user named: Superslau (I've cleaned it up a good bit for here):
This feature is really awesome. I have
implemented this with polymorphic
associations, and it works!
class Task < ActiveRecord::Base
has_many :assets, :dependent=>:destroy
accepts_nested_attributes_for :assets, :allow_destroy => true
belongs_to :workable, :polymorphic => true
end
class Upload < ActiveRecord::Base
has_one :task, :as => :workable, :dependent=>:destroy
accepts_nested_attributes_for :task, :allow_destroy => true
end
Upload is a kind of task. All tasks
can have one or more assets uploaded.
I took me a while to figure out that I
should use the Upload model as the
parent. So in one form, I can create
an upload, and it’s corresponding task
entry, along with a file upload.
in my controller:
def new
#upload = Upload.new
#upload.task = Task.new
#upload.task.assets.build
end
Don’t
worry if that doesn’t make any sense,
I just wanted to let people know that
accepts_nested_attributes_for works
just fine with polymorphic
associations. Thanks Eloy!
Very well, nested form builders doesn't have to be associated with nested resources AFAIK.Can you post your models code as well?
There is a RailsCast on Complex Forms that might help you with building a single form from multiple models.
If the car/motorcycle/truck models are identical, you should add a type column to your vehicle model. If they're not, you should use STI (single table inheritance).
But yeah, need to see your models first before I can give you code.
You can avoid this and make things a bit simpler by introducing a Vehicle model. The Vehicle model can have all your PreviousOwners, PreviousPlates, etc collections, and then your Truck, Car and Motorcycle models can has_one Vehicle.

Resources