Custom validations - for all models - ruby-on-rails

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.

Related

Default creation of has_one dependency in rails

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

How to structure this so I get all the benefits from STI with none of the consequences? (Pretty irresponsible, I know.)

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

Organization model extends user model

I have User model and Organization model. The only difference is that organization has_many users, all other properties are same. I don't want to put it in one table/model. How can I remove tons of code duplicating in this models? Should I use Concerns? I think, it will be not normal if User model will looks like :
class User < ActiveRecord::Base
include user_concern
end
So, how can I extend user model in other model? And how to generate this model with rails g with all User's fields inside?
beware STI
I would keep with concerns rather than using STI. STI often causes more problem that it solves (type mismatches, form urls, etc), plus inheritance won't make sense, here : an user is not a kind of company, and a company is not a kind of user.
That's a naming problem
I think your problem is a naming one. Obviously, your concern should not be "UserConcern". The question is : what kind of methods do you group in that concern ?
Are your methods about social relation between users ? Then, you need a Socializable concern. Are they about subscribing to mailing list ? Then you need a Subscribable concern.
It's ok to have several ones, even if they have a single method in it, because developers won't wonder "what the hell does this do ?" if all concerns are correctly named.
What to duplicate anyway
You should also probably let class level method calls out concerns.
If it's ok for scopes to be embedded in concerns (after all, they resolve in method definitions), it feels less natural to me to put relations in there.
It's ok to duplicate #has_many :foos, we do it all the time in separate models, and it's already difficult enough to get an idea of table schema from a model without hiding more information.
You could use single table inheritance (STI) for this. To get it to work, your model needs a type-field of type string, in which ActiveRecord stores the actual type of your record. Then, you just extend your base model for this.
migration
add_column :users, :type, :string
models
class User < ActiveRecord::Base and class Organisation < User.
ActiveRecord will now fill your type-field with the model-name, and store both in your users table (since this is the one the organisation model is inheriting from).
Have a look at the according section on http://api.rubyonrails.org/classes/ActiveRecord/Base.html .
However, in your case, I'd create a base model, e.g. Address, and then extend User and Organisation from it, to maintain semantically correct models.

rails - root model or application model

I was just looking around rails and noticed there is an app controller but no app model.
Is there no root model in rails ? if not where do you put a piece of code that needs to be in every model.
Thanks, Alex
Nothing says your controllers have to subclass ApplicationController but it generally is the standard because the vast majority of rails applications make use of the layout capabilities (which can vary on each controller), so instead of forcing the rare ones without layouts from turning the layout off (layout nil or layout false) for each controller, they make an 'abstract' one that you can turn controller features on and off easily for your whole application.
Now for models, you could create an ApplicationModel for all of your models to subclass, but there are two things to think about:
ActiveRecord normally detects when you subclass an already subclassed ActiveRecord::Base and uses this to turn STI (single table inheritance) on.
ApplicationModel will be an actual model that is expected to have a table in your database. This can lead to problems down the line.
To fix these two problems, you have to set abstract_class to true for ActiveRecord to properly function.
class ApplicationModel < ActiveRecord::Base
self.abstract_class = true
end
In contrast to an abstract ActionController, abstract_class must set to true which means the developer must know they cannot remove this line from ApplicationModel. With ApplicationController you can do pretty much whatever you want to it.
Most models in rails inherit from ActiveRecord::Base which has all of the conventional getters and setters and the association methods, validations and support.
You could extend ActiveRecord::Base in a file in the lib directory (or anywhere in the rails load path really) and any models that are active record models would have the method(s) available. Modules are also a good way of sharing code between many models.
I think I remember some years ago, there actually existed a ann AppModel by default. Is this true or do I mix things up (I haven't been working with Rails for some years until now)?

ActiveRecord Inheritance with Different Database Tables

I have just started investigating using more advanced models in Rails. One that I use regularly, with great success, is model where a many-to-many cross-reference relationship is accessed by a class that itself is a sub-class of the base class in the many-to-many relationship.
This way the cross-reference class can act as a stand-in for the base class.
A good example is where a navigation hierarchy node (NavigationNode) is cross-referenced to a user role. At the cross-reference point a class (RoleNavigationNode) can inherit from NavigationNode and still have intimate knowledge of the user role.
My question is (in the case above) can RoleNavigationNode inherit from NavigationNode and yet access the cross-reference table rather than the one that NavigationNode accesses -- this of course using ActiveRecord.
I have not investigated polymorphic association, which may be more appropriate.
Thanks in advance...,
tried set_table_name on the subclass?
Also, look into setting #abstract_class in model classes.
Lastly, what you need may simply be a Mixin that you include in both models.
Anyway, what you're trying to do sounds rather un-ActiveRecord-ish. You might want to post a clearer example of what you're trying to achieve, maybe we'll be able to come up with something simpler.
This works in Rails 3:
class Common < ActiveRecord::Base
#abstract_class = true
def common
"Foobar!"
end
end
class Model < Common
end
class AnotherModel < Common
end
Without setting abstract_class, Rails will look for a table named commons in your database.

Resources