I'd like to understand best practices for creating a dependency of a model in rails. The scenario is simple. The two models are:
class Main < ActiveRecord::Base
has_one :dependent
validates :dependent, presence: true
end
class Dependent < ActiveRecord::Base
end
(Note that I want to validate to ensure the dependent always exists)
Whenever a Main object is created I want a Dependent object to be created and "default initialized". I come from a background of C++ hence I view this problem as one of constructing a member variable whose type is some class which has a default constructor.
There are a bunch of ways I can solve this.
Put logic in before_validation to create a Dependent.
This feels very "un-railsy". I wanted to do this in before_create but validations are done before that callback. Doing it in before_validation is ugly as this callback is called both on create and on update, which makes the logic tricky/messy.
Put logic in .new
This feels very very "un-railsy" and is probably conceptually wrong. I'd see new as performing ActiveRecord construction which happens before the model is built.
Make the caller do the work
Whenever a Main object is created it must be done via new-save rather than create. The calling code then has to create Dependent itself, albeit with default values, e.g.
Main.new do |m|
m.dependent = Dependent.create
end
This is annoyingly burdensome on callers and causes a lot of duplicate code. I could pack this into a factory type method but the same problem exists that calling code needs to do some legwork.
Is there a canonical solution to this?
You should try using after_create callback and create Dependent instance in this method.
To answer my own question, I found a callback I hadn't see listed before: after_initialize. I can do what I want here.
A note for others:
My particular case is quite straightforward as I always want my dependent class to be default initialized and the user doesn't ever need to set anything. However, in a more complex situation this wouldn't work and initializing dependents may require:
Explicit initialization at the site of creation
UI for user to initialize the dependent using #accepts_nested_attributes_for
Related
Say I have models Foo, Bar, and Baz. There is a validation I want to perform on fields in each of these tables. Since it is the same validation, and I like DRY code, and I'm lazy, I'd like to write the logic for this validation in one place and be able to reference it in the model without having to write more than a single line in each model - Much like making methods available to multiple controllers with the use of ApplicationController.
The implementation would look something like this:
class Foo < ActiveRecord::Base
validates :some_field, :custom_shared_validation
end
class Bar < ActiveRecord::Base
validates :some_other_field, :custom_shared_validation
end
But I'm not sure if this is possible, or, if so, where I am to write it. I do know that one way of extending ActiveRecord::Base is to write Concerns, which I'm under the impression allows you to add methods accessible to all models, but I'm not sure if this practice extends to using these methods as validators in all models.
It's incredibly straightforward to do this with concerns...
module OmniVerifiable
extend ActiveRecord::Concern
included do
validates :field, :custom_shared_validation
end
end
...but that's if the fields are the same across all of the models you're validating.
If they're not, then you've got to choose between the worse of the two: either you author a function to bring back the custom field that you want in each model, or you bite the bullet and write the validation in each model.
My advice would be to write out the validations individually since conceptually they concern different fields. If they're different fields, then you'll be doing repeat work anyway. Don't take DRY too far as to make your code terse and unreadable.
See http://guides.rubyonrails.org/active_record_validations.html#performing-custom-validations
Just to recap in case that link changes at some point, in Rails 4 you would write a class that subclasses either ActiveModel::Validator or ActiveModel::EachValidator. That class needs to implement either a validate or a validate_each method respectively. Then you can use it in any of your models, or any class that has ActiveModel::Validations mixed in.
Say I have the following example of associations in a Rails app:
I'm considering combining the *Posting models under STI. One problem with STI is the potential for many attributes that are only related to one subclass (i.e., a lot of denormalized nil values). This is especially worrisome when your subclasses and going to evolve and grow in the future. I've read a few related posts (such as this), however, as you can see in my example, the potential subclass-specific fields will not necessarily be just attributes, but rather, a lot of belongs_to associations.
My question is, how could I restructure this to use STI from a Posting model for all the common attributes/methods (of which there will be quite a few in my actual app), but keep the unique subclass-specific attributes and belongs_to associations from piling up in the Posting model? Also, the ability to access #board.postings and work with those standard methods is important.
For example, I've thought about moving the type-specific attributes to another model:
class CarPosting < Posting
has_one: car_posting_detail
end
class CarPostingDetail < ActiveRecord::Base
belongs_to :car_posting
belongs_to :car_make
belongs_to :car_model
end
Although, this starts to create a lot of joins, I'm not sure I have the has_one/belongs_to declarations in the right direction, and you have to start chaining calls (e.g., #posting.car_posting_detail.car_make).
Are there other design patterns you have seen for accomplishing this?
You basically have to 2 options for accomplishing inheritance.
First, you can use rails STI as you suggested. The downside is that end up with nil attribute for the child classes that do not use all of the fields. Your idea to reduce this by adding type-specific attributes to another model is a great way to reduce this. However, you should keep the implementation as DRY as possible by defining a has_one :detail for the Posting. Then you can simply assign specific detail types in the Posting childs. For example, CarPosting's detail would be CarPostingDetail. This is convenient because then all Posting children will have their details accessed identically, but will still have different details. So the query now looks like #posting.detail.car_make. To take this one step further, you can define a custom helper method in your Posting model to grab each attribute in the current Posting's detail and create an accessor for it. Now the entire detail layer is transparent and you can simply access those attributes by saying #posting.car_make.
Second, you can use an abstract class. This is essentially the reverse of STI. You create an abstract model class which can never be instantiated. Thus, you cannot define any relationships in the Posting class because it has no table. Each child of the abstract Posting class has its own separate table. The main advantage of doing this would be the ability to define methods for all of your Posting types without copy and pasting them into every model. So this options is better if there are some overlapping functionality across the models, but very little data overlap.
You could use polymorphic associations for this.
Post model belongs_to :postable, :polymorphic => true
car, event and all the other "postable" classes would have this relationship
has_many :posts, as: :postable
Post would hold the postable_id and postable_type
More info here
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
I wasn't sure how to get it to work syntax wise. I did get it to work like the following though:
scope :out_of_stock, lambda{ |company| get_inventory(company, 'out_of_stock') }
scope :in_stock, lambda{ |company| get_inventory(company, 'in_stock') }
def self.get_inventory(company, stock_status)
StockInventory.find_all_by_....
end
However, is there a way to do it without the anonymous functions?
First of all, there are no static functions in Ruby. The self.get_inventory is a class method, which is in effect a singleton instance method on the Company class. The scope call is itself a class method that dynamically defines other class methods, in this case out_of_stock and in_stock.
Second, it's hard to tell what you're trying to do, since you didn't include your entire method, but I'll assume you're trying to get the instances of StockInventory that have a particular company id and inventory status.
ActiveRecord allows you to use finders, as it appears you've done here, or relations, which are a bit more flexible, as they are typically chainable, and because their execution is delayed until absolutely necessary, meaning you can pass them around to different methods without hitting the database. Unless I'm looking up a single object with a known id, I usually stick to relations for this reason.
I'm going to make a few assumptions, since your question isn't particularly clear. If you have defined a relation between Company and StockInventory (like has_many :stock_inventories and belongs_to :company), you can use that relation as a starting point and defined a method like this on the StockInventory class:
def self.in_stock
where(stock_status: "in stock") # or whatever
end
Note that instead of passing in a Company instance as an argument to a Company class method that ultimately loads some other class (this should be a red flag), you call this directly on the StockInventory class or any StockInventory relation. Plus, since it's a relation, you can chain other ActiveRecord methods to it, i.e. my_company.stock_inventories.in_stock.limit(10).
You'll need to alter this method to fit your particular database columns and statuses, but this is as complicated as it needs to get.
Whatever the case, I recommend you read the Active Record Query Interface guide - there's a lot you can do with it once you understand how it works.
A model I've implemented needs to escape/format one of the fields into an html-friendly version of the entered text (for the sake of argument, lets say it's a blue/redcloth stlye thing).
I implemented this in an observer with the before_save callback, all working, no complaints. I am learning rails as I code and it struck me that from just looking at the model there is no indication that it has an observer.
Is there a neat way of indicating (for other programmers who may view/edit my code) that a model has an observer or should I just do this in a comment (or not at all).
As far as I know, you do not need to specify on the target class/model that it has an observer all you need to do is make sure that the observer lists which models it is observing.
class ContentObserver < ActiveRecord::Observer
observe :answer, :audio_clip, :document
#This right here ^
def after_update(record)
record.recent_activity.save!
end
end
Have a spec/test file for the observer. If, in the future, someone changes the model in a way that upsets the observer then the spec/test will fail which will alert them.
I've just been reading this question which is about giving an ActiveRecord model's date field a default value. The accepted answer shows how to set the default value from within the controller. To my mind, this sort of business logic really belongs in the model itself.
Then I got to thinking how if this were Java I'd probably set the initial field value when declaring the instance variable or within the constructor. Since database-backed fields don't have to be explicitly declared within ActiveRecord models, is this something that you could use the model's initialize method for? I'm curious because I've not really seen much use of constructors for ActiveRecord models within the Rails code that I've looked at. Do they have a role to play and if so, what is it?
I do this quite often actually for default values. It works well and still lets the user change it. Remember, the initialize method is called when you say MyObject.new. However, you may want to read this blog entry (albeit a bit outdated) about using initialize.
You should use after_initialize instead of initialize. The initialize method is required by ActiveRecord::Base to prepare many of the convenience methods. If an after_initialize method is defined in your model it gets called as a callback to new, create, find and any other methods that generate instances of your model.
Ideally you'd want to define it like this:
def after_initialize
#attribute ||= default_value
end
Also note, you cannot use this callback like the others, you must define a method named after_initialize (like above) for it to work. You can't do the following:
after_initialize :run_some_other_method
#TopherFangio's answer is correct. It seems that the ActiveRecord API changed some time between his answer (2009) and now (2015).
As of today (Rails 4 with ActiveRecord 4.2.0), here's how you add initializers according to the ActiveRecord docs:
class Widget < ActiveRecord::Base
after_initialize |new_widget|
new_widget.name ||= 'Unnamed Widget'
end
end
You can verify with puts statements or by inspecting the new object from rails console that it actually initializes correctly.
According to this blog, active record doesn't always use new, so initialize might not be called on your object.