I'm trying to test my Order AR model in "isolation", but because it lives into a complex "context", I need to create a lot of associated AR models before.
My models are:
class Order < ApplicationRecord
belongs_to :registration #it's required!!!
end
class Registration < ApplicationRecord
belongs_to :event #it's required!!!
end
class Event < ApplicationRecord
has_many :registrations
belongs_to :account #An account belongs_to an organization
#This model a couple of required associations also
end
Is some way to "mock" the associations of my Order model??? Should I?
Thanks in advance!
I think, that with proper factory_girl setup, there will be absolutely no issues to easily setup context, that you need. During my experience I haven't faced any cases when associations were stubbed.
From my point of view, if you have some complex method inside your Order model, then you should move it into separate entity (service object, separate library, decorator). Then you can easily test this logic, without any need to setup associations, objects, etc.
I don't remember the last time I really needed to test my modal in isolation after I start making correct design of my application. I suspect, that your issue is also like a bell for you about your design. I treat models only as a set of validations(tested inside rails), associations(tested inside rails) and trivial methods, so usually there is no need to test your models
Related
I'm refactoring some piece of code that deals with polymorphic belongs_to association that can receive a model that implements Single Table Inheritance. The current code follows an advice from ActiveRecord documentation here, namely that it is best to override attachable_type= so that the base class name of the STI model gets stored in the attachable_type field:
class Asset < ActiveRecord::Base
belongs_to :attachable, polymorphic: true
def attachable_type=(class_name)
super(class_name.constantize.base_class.to_s)
end
end
class Post < ActiveRecord::Base
# because we store "Post" in attachable_type now dependent: :destroy will work
has_many :assets, as: :attachable, dependent: :destroy
end
class GuestPost < Post
end
class MemberPost < Post
end
I understand that we want to store that base class so that ActiveRecord works as expected. However, when I tested a few scenarios including the scenario in the documentation, it seems that overriding attachable_type= is unnecessary. ActiveRecord seems to already handle this case and always stores the base model Post rather than GuestPost or MemberPost.
I created a repo that implements the models in the documentation and allows you to play with some data and see for yourself. I cannot reproduce the issue that the documentation attempts to circumvent all the way up to ActiveRecord version 3.2.22.5 (I haven't tried beyond that).
Does anyone know if there is truly a need to override attachable_type=? Is the documentation out of date? Or is it just solving the use case of directly running Asset.new attachable_type: 'MemberPost', attachable_id: member_post.id instead of Asset.new attachable: member_post?
The reason why this matters to me is that we have 5 models that could be affected by this. Our current code implements overriding 3 of those, and I'm refactoring to DRY it up and potentially introduce it to the additional 2 other models in which we forgot to follow this advice.
OK, I believe I better understand the reasoning behind it. This is an issue when the model needs to be created or updated from a form.
In such scenario, attachable_type and attachable_id are sent to the controller, which typically passes-through this data to the model. Without overriding attachable_type= the model will end up having attachable_type be one of the child classes GuestPost or MemberPost.
This in turn causes a range of issues. For example, assets won't be destroyed when the owner of the asset is destroyed even when dependent: :destroy is specified on the has_many association.
Therefore, if your model is expected to receive data for the polymorphic association field via a form, you should override this method.
I updated the repo to demonstrate this issue.
I'm practicing ruby on rails and I'm trying to add achievements to users. I was wondering if anyone could explain the benefits/detriments to using a hash over a new class.
For example, should my object have a "has_many" relationship with an "achievement" object, or should there be a "achievement" hash and why? I'm mostly concerned with database speed implications.
should my object have a has_many relationship with an "achievement" object, or should there be a "achievement" hash and why?
I'd most certainly recommend a has_many relationship based on database backends - it gives you the ability to build the associative data as you require (instead of messing around with custom methods).
You must also realize that ActiveRecord will build a hash for you, right?
The only difference is that ActiveRecord will populate the hash with data from your db, whilst I believe you'll be talking about a hash of static data (which I've not massive experience with).
--
Since this question doesn't have many answers, this is how I'd do it:
#app/models/achievement.rb
class Achievement < ActiveRecord::Base
## you could attach this to MongoDB or some other file-based storage system
has_many :awards
has_many :users, through : :awards
end
#app/models/award.rb
class Award < ActiveRecord::Base
belongs_to :user
belongs_to :achievement
end
#app/models/user.rb
class User < ActiveRecord::Base
has_many :awards
has_many :achievements, through: :award
end
This is a standard has_many :through relationship. I imagine you already know about it, so I'll not bore you with details; however, you have to remember that this type of setup is a standard in Rails -- it will give you the functionality you want without any of the customization your ideas will probably require.
It will give you the ability to call the following:
#awards = Award.joins(:achievements).where(user_id: #current_user.id)
This will take one DB call to bring back all the achievements for a single user, all related.
The data will be encapsulated in classes -- and will basically be a series of hashes, populated from the db.
The Achievement model could easily be converted to use static data.
I am using Ruby on Rails 3.2.2 and I would like to know what is a common approach in order to handle associated objects of a has_many :through ActiveRecord::Association. That is, I have:
class User < ActiveRecord::Base
has_many :article_editor_associations, :class_name => 'Articles::UserEditorAssociation'
has_many :articles, :through => :article_editor_associations
end
class Article < ActiveRecord::Base
has_many :user_editor_associations, :class_name => 'Articles::UserEditorAssociation'
has_many :editor_users, :through => :user_editor_associations
end
class Articles::UserAssociation < ActiveRecord::Base
belongs_to :editor_users
belongs_to :articles
end
By using the above code, I can run the #article.editor_users method so to retrieve an Array of Editor Users. However, in order to make things to fit better with my application (that is, for example, in order to handle things like I18n translations and similar in a "programmatic" way), I am thinking to add to my application a new model like the following:
class EditorUser < User # Note the class name and the class inheritance
...
end
This way, through my application, I can refer to the EditorUser class in order to handle article "editor user" instances as if they were User objects... more, since inheritance, in the EditorUser class I can state "specific methods" (for example, scope methods) available only to EditorUser instances (not to User instances)...
Is it a "common" approach to make things as I would like to make in my case? Is it the "Rails Way"? If so, what I could / should make to handle this situation? If no, how could / should I proceed?
In other words, I thought using class EditorUser < User ... end because associated EditorUser objects (retrieved by running the #article.editor_users method) are User objects. I think that by stating a EditoUser class in the app/models directory (or elsewhere) could simplify things in my application because you can work around that constant name (for example, you can "play" with that constant name in order to "build" translation strings or by stating new methods just to be used for EditorUser instances).
With Rails, I've learned to focus on the naming conventions and standard usage first ('convention' over configuration) and would set it up like this:
class User < ActiveRecord::Base
has_many :editors
has_many :articles, :through => :editors
end
class Article < ActiveRecord::Base
has_many :editors
has_many :users, :through => :editors
end
class Editor < ActiveRecord::Base
belongs_to :user
belongs_to :article
end
You can either use the presence of the join record, e.g. User.editor or add an additional attribute to editor if you want different editor access levels.
The above does not fully answer your question perhaps but should be a good starting point. I say this because one of the most important things about rails is that it uses a principle of 'convention over configuration'. This is good as it leads to terse, minimalist code. It's bad because you have to learn all the zillion conventions. If you don't know them or the framework well you can get yourself into a whole heap of trouble as I have seen with many rails applications that I have worked on over the years.
So my advice is really to step back. Don't try and force things to work with things like class renames. If the setup I have shown doesn't meet your needs, revisit your needs and read more on active record and associations in the API. I know this can be kinda frustrating for quite a while with rails but you really need to look how to do things the right way if you're going to be a good rails programmer in the long term.
So I have the following models in my Ruby on Rails setup: users and courses
The courses need to have content_managers and those content_managers are made up of several individuals in the users model.
I'm a newbie, so bear with me. I was thinking of creating a new model called content_managers that has a user_id and a course_id that links the two tables. It makes sense to me that courses HAVE content_managers. However from the users model, it doesn't make sense that users HAVE content_managers. Some of them ARE content_managers.
From that point of view I believe I'm thinking about it incorrectly and need to set up my ActiveRecord in a different manner from what I'm envisioning. Any help is appreciated.
Thanks!
There's no "have" or "are" in ActiveRecord, only "has_many", "has_one" and "belongs_to". With those tools you should be able to do what you want.
An example:
class Course < ActiveRecord::Base
has_many :content_managers
end
class ContentManager < ActiveRecord::Base
has_many :content_manager_members
has_many :users,
:through => :content_manager_members,
:source => :user
end
class ContentManagerMember < ActiveRecord::Base
belongs_to :course_manager
belongs_to :user
end
class User < ActiveRecord::Base
has_many :content_manager_members
has_many :content_managers,
:through => :content_manager_members
end
Be sure to index these correctly and you should be fine, though navigating from User to Course will be slow. You may need to cache some of this in order to find the level of performance you want, but that's a separate issue that will be uncovered during testing.
Whenever implementing something like this, be sure to load it up with a sufficient amount of test data that will represent about 10x the anticipated usage level to know where the ceiling is. Some structures perform very well only at trivial dataset sizes, but melt down when exposed to real-world conditions.
I have some problem trying to understand when building a rails app with several models and relation ships between them...
If I take a basic example like a model Group, a model User and a model Car
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
has_many :cars
end
class Car < ActiveRecord::Base
belongs_to :user
end
Will those relation ship statements automatically create the following functions:
group.users
user.group
user.cars
car.user
It seems that we sometimes need to have to create "references" in migration (like adding a reference toward User in Car table) but is this always required ?
In this case, what is the difference of creating the migration and of adding the relationship statement in the models ? I sometimes have the feeling this is used for the same purpose.
Thanks a lot for your help,
Regards,
Luc
The association declarations are there for Rails only. You have to define the foreign keys (references) in the database, so that Rails can properly save the data.
Remember, despite all the magic, it's still backed by a relational database, so good practices there will pay off in the long run.