I'm trying to make objects belong to other objects of the same class by adding a parent_id attribute to the object so active record can associate them with each other in a hierarchical manner.
I know I can just write an instance method to do a find and get them, but I want to know if there is a more rails appropriate way to accomplish this, so can I set this up with active record associations, if so how?
It seems that this gem may be of help :
https://github.com/skyeagle/nested_set
or acts_as_tree:
https://github.com/rails/acts_as_tree
There are a lot of ways of nesting data in databases. The one to choose manly depends on how you are going to access this data, and how often you are going to change the tree.
Here is a list of current nesting plugins for rails: http://www.ruby-toolbox.com/categories/activerecord_nesting.html Be sure to reed each of the gems documentation in order to choose the most appropriate one for your situation.
Related
I have a project that is built with a tagging model to reference three different models: artist, article, event. I have associated each model to the tagging model via has_many through:. I have two problems; both related to each other:
I have 5 "default" methods that I wish to be able to call restfully from the tagging controller/model: popular, upcoming, events, articles, and artists. Each method is designed to do exactly as it's name implies. The issue lies within these methods being a query by nature. How can I maintain a consistent schema where 5 of the many taggings models I have require a special attribute (let's call it content) that should subsequently call the appropriate method?
When calling any one of the taggings models, it will return (in it's content attribute) an array of the 3 models specified earlier. Other than adding a method within each model that contains a localized description of the model and then calling upon that type method to match another hardcoded string elsewhere, is there any alternative? I don't like how the implementation I just described requires me to hardcode values. Eek.
I'm very new to Ruby on Rails, so I apologize if this is an obvious solution. However I have spent a week looking into ways to solve this compound problem that I'm trying to solve. Any input is appreciated!
As far as i unterstand you need a polymorphic association. Because tags can be applied on different models, the clue is to treat all these models in polymorphic manner by marking them as :taggable on the association to tags.
I would just use the popular acts_as_taggable_on gem for it.
I have a model and table that I believe is perfectly suited to STI. My table is called Finances and has two types: Income and Expenses. Besides type there are three other columns: description, amount, and date.
I'm getting very nervous using STI in Rails, since it requires some hacking. I'm too new to Rails to hack up the code. Even though it works, I don't understand it. That seems dangerous.
My question is, how do I set up my model, controller, and view if I do NOT use STI? Any best practices to group items in my model? Or do I just do Finances.where("type = 'Income'") before setting up a view?
Edit: I made a gist to show the code I'm working with. When I run it I get the error:
undefined method `incomes_path' for #<#<Class:0x007fbc95f60b40>:0x007fbc93883220>
First, using STI is standard for Rails, so there is no need to feel nervous. And no need for "hacking".
It has been used very successfully by many developers. And as you have seen, you can find tutorials and general information on the net.
If on the other hand, you decide NOT to use STI, you have the choice of using
(a) completely separate models with their own tables, which will result in a lot of duplicated code, or
(b) create you custom "STI-like" behaviour by hand.
The second option could at least be interesting to learn more about Rails.
For example, in your Finances model you would define a scope incomes, like
scope :incomes, where(:type => 'Income')
then you can do Finances.incomes.
Then, if you have methods that apply only to one of the types, you should check that all records effectively are of the needed type.
Personally, I would advice you to use STI. You get a lot of functionality for free and you are doing it the Rails way.
Imagine, for example, other developers reading your code, they will ask themselves, why you didn't use STI, blame it on ignorance and - if need be - refactor it using STI.
STI is best if you are using inheritance structure like this. You don't really need to use Finances.where("type = 'Income'"). You can simply use Income.all. see these posts if they help you.
http://www.therailworld.com/posts/18-Single-Table-Inheritance-with-Rails
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
I have a need for a certain model to contain a reference to a document. Most of the model could be stored in postgres. The model is for a "level" in a game. I'd like to store the level data itself inside of a document, which makes more sense than making a complex tree in sql.
I am able to use postgres with mongoid installed; however, after installing the mongoid gem I seem to only be able to scaffold mongoid (non active record) documents.
The problem is that I have references to other tables, and I don't neccesarily know how to link that up within a mongoid model.
Questions:
How can I force scaffolding to occur with active record instead of mongoid or vice versa. Edit: partly answered here: Using Active Record generators after Mongoid installation? (2nd answer works, but I don't know how to go back and forth easily)
Is there an easy way to reference a document from an active record model (I know the documentation said don't mix them, but it is ideal for what I am trying to do).
If it is not possible to mix them, then how should I make a document be referenced from a postgres/active record table. In other words how can I get both pieces of data at the same time?
Thanks!
Regarding your first question, the ideal solution would be something along the lines of the first answer in the referenced post. However, instead of a generating a migration, generate a model instead. So when you want an Active Record model simply run:
rails g active_record:model
As for your second and third questions, to associate an Active Record model with a Mongoid document simply store the ObjectId as a string in the model. Then, when you get retrieve a record make a new ObjectId out of the string and use that to query for the related document.
You can create object ids out of the strings like this:
BSON::ObjectId.from_string("object_id_string")
There isn't really an easy way to easily follow intra-orm relations when mixing and matching between ActiveRecord and Mongoid though so I'm afraid that will have to be done via Ruby code.
The models you define in rails either extend one ORM's base class or the other and they don't know about one another. There may be projects out there that act as a layer on top of these ORMs but I am not familiar with any that exist at the moment.
I am using Ruby on Rails 3.0.7 and, for performance reason, I would like to avoid loading associated objects on retrieving a class obect. That is, if I have an Article class\model with a has_many :users statement I would like to not load associated User objects when I retrieve an Article object (I think this behavior depends on the Ruby on Rails "Convention over Configuration" principle).
How can I do that?
As noted by Yet Another Geek, Rails (ActiveRecord) doesn't load the relationship objects by default. Rather, it goes and gets them when you ask for them. If you don't need the objects of that relationship, it will never bother to load them, saving database time.
If you do need then, it will go retrieve them lazily (by default). If you know you'll need all (or many) of the objects of the relationship (assuming x-to-many), then you can use the :include modifier to your find to get them all up front (which will be a lot faster since it can do that with a single db call). Knowing and taking advantage of the ability to eagerly load relationship objects is an important thing.
#person = Person.find(params[:id], :include => :friends)
All that being said, the behavior you want (not loading the objects if you don't need them) is the default behavior and you should be all set. The rest of the answer was just some context that may be useful to you later.
Implied by this wiki article, loading is lazy by default. You have to include the :users relationship if you want it eagerly loaded.
I've come across an oddity in ActiveRecord's #relationship_ids method (that's added automatically when you declare 'has_many'), which saves immediately for existing records, which is causing me some issues, and I wonder if anyone had any useful advice.
I'm running Rails 2.3.5.
Consider this simple scenario, where an article has_many tags, say:
a = Article.first
a.name = "New Name" # No save yet
a.author_id = 1 # No save yet
a.tag_ids = [1,2,3] # These changes are saved to the database
# immediately, even if I don't subsequently
# call 'a.save'
This seems surprising to me. It's specifically causing problems whilst trying to build a preview facility - I want to update a bunch of attributes and then preview the article without saving it - but in this instance the tag changes do get saved, even though no other fields do.
(Of possible relevance is that if 'a' is a new article, rather than an existing one, things behave as I'd expect - nothing is saved until I call 'a.save')
I have a fairly nasty workaround - I can override the tag_ids= method in my model to instead populate an instance variable, and actually save the related models in a before_save callback.
But I'd love to know of a simpler way than me having to do this for every model with a has_many relationship I'd like to create a preview facility for.
Does anyone have any fixes/workarounds/general advice? Thanks!
There's a reason things are this way. It's called foreign keys. In a has many relationship, the information that links to the model that has many is stored outside of that model as a foreign key.
As in Articles, has many tags. The information that links a tag to an article is stored either in the tags table or in a join table. When you call save on an article you're only saving the article.
Active record modifies those other records immediately. Except in the case where you're working with a new article that hasn't been saved yet. Rails will delay creating/updating the associated records if it doesn't know which id to place in the foreign key.
However, if you're modifying existing records, the solution you've decided on is really all that you can do. There's an even uglier hack using accepts_nested_attributes_for, but it's really not worth the effort.
If you're looking to add this behaviour to many models but not all models, you might want to consider writing a simple plugin to redefine the assigment the method you need and add the call back in a single class method call. Have a look at the source of something like acts_as_audited to see how it's done.
If you're looking to add this behaviour to all models, you can probably write a wrapper for has_many to do that.