What is the best way to abstract an ActiveRecord attribute without further normalizing the database?
For example, let's assume a database table named addresses with a column zip_code and a method to determine if the zip code is valid:
class Address < ActiveRecord::Base
def zip_code_valid?
..
end
end
I would prefer to have:
class Address < ActiveRecord::Base
..
end
class ZipCode
def valid?
..
end
end
and when I execute Address.find(1).zip_code, it returns a ZipCode vs. a string. I would prefer not to normalize the database by creating a table called zip_codes. The example is hypothetical, and I currently do not have a real world example of this; I simply want to know how I could potentially do this.
Thank you.
I'm not sure why you'd want to do this for ZipCode as you discussed, but to answer your question, you should consider using Rails Aggregations.
Here's the documentation :
http://api.rubyonrails.org/classes/ActiveRecord/Aggregations/ClassMethods.html
If you have specific questions about things you want to accomplish, let me know and I can try to answer those specific questions.
I don't think abstracting the zip code out into a class makes sense if you're not going to do the same with the street, house number, city, etc. You already have it as a separate column in the Address table, so it doesn't make sense to store it in its own class (from an ActiveRecord point of view). Alternatively, if you store zip codes in their own table (and hence class), what are you gaining? In my opinion it's going too far to keep a single attribute in a separate class/table if it makes sense as to be part of the aggregate that it's currently in.
Related
I have an application where a User can create many Links, and each Link can store different type of data, depending on what type of Link it is. For example, a TelephoneLinkData stores a telephone number, an EmailLinkData stores an email address, a subject and a body. Each Link also has some fields in common, such as a reference to the user and a name.
I've tried to map this into ActiveRecord as cleanly as I can. Currently, I have a polymorphic relationship from Link to its data-type:
class Link < ApplicationRecord
belongs_to :user
belongs_to :link_data, polymorphic: true
...
class EmailLinkData < ApplicationRecord
has_one :link, as: :link_data
accepts_nested_attributes_for :links
...
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes, what I'm trying to model is a class having multiple possible different child classes. This works fine, and I'm able to create Links through the various *LinkData controllers, but what I'd really want to do is have the Link act as the primary source of interaction for the user, so that the user manages their links through the /links path. For example, I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
I've looked around for other ways to model this relationship, and the most common other suggestion seems to be Single-Table Inheritance, but the majority of my columns will differ between LinkData classes, so that feels like the wrong abstraction.
Is there a more idiomatic way to model this data structure?
As is always the case, the best choice depends on the business or application needs, so it's difficult to provide a recommendation without knowing more about what you're trying to do.
It sounds like you prefer the MTI approach, essentially using actual foreign keys and an XOR constraint to the Link table instead of a type column. That's a totally reasonable (although not as common) alternative to a polymorphic association.
However, I think there was a bit of a misunderstanding in your question.
Technically, I think this would be described as a reverse polymorphic relationship as instead of a class having possibly different parent classes...
A polymorphic association in Ruby/Rails doesn't have anything to do with class inheritance (e.g. parents and children). You might be thinking of Single table inheritance. A polymorphic association allows one class (e.g. a Link) to be associated a record in any other table (e.g. the various classes of LinkData) via two fields, a association_id and association_type. These associated classes need not be related to each other. For example, a common use case might be the acts_as_commentable gem, that allows you to add a comment to any other object, and the comment would have a polymorphic association with the other classes.
In the second part of your question you mention that you'd like the User to interact with Link's via a single controller.
I would like the API to allow a User to create a link by posting to /links with the data for the LinkData nested in the link_data field
There's nothing stopping you from implementing this using the initially proposed data model. ActiveRecord may not handle this completely for you out of the box, but you can imagine implementing a link_data= method on the Link class that would create the appropriate associated object.
I'd say the pros/cons of using a polymorphic association would be...
Pros:
easy to setup and use
easy to make required (validate presence of / not null)
easy to associate with a new class
Cons:
no referential / database integrity
have to migrate data if you change a class name
And using the MTI approach is basically the opposite. A bit harder to setup and use, harder to add a new association/table, harder to ensure exactly one association exists... but the long term data quality benefits are significant.
I was able to get things to work the way I wanted to using multiple table inheritance, based largely on this chapter: https://danchak99.wordpress.com/enterprise-rails/chapter-10-multiple-table-inheritance/
This is a pretty fundamental question, I guess, but I'm having a hard time finding an answer. It may be that this just isn't a thing people do, and if that's the case I'd like to know why.
In a show action, usually I have some code like this:
#structure = Structure.where(id: params[:id])
This gets me a relation containing the unique structure with the given id. Structure has_many plannings. What I'd like is a way for my "Structure" relation to contain structures that have plannings relations that have been filtered. So, I don't want the Structures scoped to their plannings, I want the plannings on those structures to themselves have been filtered. I want to get back a relation containing a structure that has only a subset of its plannings. Does this even make sense?
edit:
I'm starting to see why this doesn't make sense, but also having a hard time articulating it. This,
#structure = Structure.where(id: params[:id])
#structure.plannings = #structure.plannings.where(audience_ids: "1")
throws an error along the lines of Cannot modify association 'Structure#plannings' because the source reflection class 'Planning' is associated to 'Course' via :has_many. The message I'm getting here is: what you are trying to do is more complicated than you think.
I guess I should just return json that looks like what I want it to look like, rather than trying to modify the relation in place.
If your criteria are constantly the same for all plannings that are fetched as a structure's relation, you could use a default scope in the relation (available on Rails 4)
class Structure < ActiveRecord::Base
has_many :plannings -> { where(attribute_a: true) }
end
that way
Structure.where( ... ).first.plannings
will apply the desired scope to plannings.
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.
I'm reading "Rails AntiPatterns" at the moment, and one of the first patterns mentioned is the Single Responsibility Principle. At this point I've encountered SRP enough times to realize that it's a fundamental concept for beginners like me to understand, which is why it's so frustrating that it's not clicking yet.
The book gives an example of an Order class:
class Order < ActiveRecord::Base
def self.find_purchase
#...
end
def self.find_waiting_For_review
#...
end
def self.find_waiting_for_sign_off
#...
end
def self.advanced_search(fields, option = {})
#...
end
def self.simple_search
#...
end
def self.advanced_search
#...
end
def to_xml
#...
end
def to_json
#...
end
def to_csv
#...
end
def to_pdf
#...
end
end
To illustrate SRP, the book recommends extracting out the 4 instance methods into a separate OrderConverter class. This makes sense to me. But at the same time, this OrderConverter class could still have multiple reasons to change:
If the application no longer requires one of the 4 formats mentioned,
the corresponding method would have to be deleted.
If the application
needed to convert to other formats, more methods would need to be
implemented.
If the method used to convert Order instances to different formats is
changed (assuming they all use the same method with a different parameter
which corresponds to the format required).
Wouldn't it be even more "correct" to separate each of these methods into a separate converter class (i.e. PdfConverter, CsvConverter, XmlConverter, etc.)? That way, the only reason for each converter class to change would be if the conversion method itself changed. If a new format was needed, a new class could be created. And if an old format is no longer needed, the class could simply be deleted. And should the original Order model really be responsible for finding instances of itself? Couldn't you separate the 'find' methods into a separate 'OrderFinder' class?
I've read the SRP chapter of Sandi Metz's "Practical Object-Oriented Design In Ruby", and she recommends the following test to see if a class has a single responsibility:
How can you determine if the Gear class contains behavior that belongs somewhere
else? One way is to pretend that it's sentient and to interrogate it. If you
rephrase every one of its methods as a question, asking the question out to make
sense. For example, "Please Mr. Gear, what is your ratio?" seems perfectly
reasonable, while "Please Mr. Gear, what are your gear_inches?" is on shaky
ground, and "Please Mr. Gear, what is your tire(size)?" is just downright
ridiculous.
But taken to an extreme, if a model has more than one attribute (i.e. a car has a # of doors and a color) with corresponding attr_accessors, isn't this already a violation of SRP? Doesn't each new attribute add another possible reason for the class to change? Clearly the answer is not to separate each of these attributes into separate classes. So then where does one draw the line?
You write about the OrderConverter class but you didn't show the source of that class. I assume that this class contains methods to_xml, to_json, to_csv and to_pdf.
Wouldn't it be even more "correct" to separate each of these methods into a separate converter class (i.e. PdfConverter, CsvConverter, XmlConverter, etc.)?
Yes, it's propably a good idea to separate these methods to converter classes: each converter class will be responsible for only one format (one responsibility).
... if a model has more than one attribute (i.e. a car has a # of doors and a color) with corresponding attr_accessors, isn't this already a violation of SRP?
Nope, these attributes (like color, no of doors, ...) are not a set of responsibilities! The responsibility of Car class is describing a Car (holding an information about a car). Each instance of car will describe one car. If a car for example is a model class (let's say you want to store a cars in DB and one instance of car is one row in DB) then you have to change the Car class if you want to change a way of describing a car in your system.
But if a car will have defined for example a producer, and a producer will be described by name and address then I would extract the details of producer to other classes because this is a responsibility of describing a producer of a car and not a Car itself.
One more thing. The SRP is not a pattern. This is a Principle (first in SOLID). This term was introduced by Robert Cecil Martin. Here http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod you can find more information.
You seem to decide that extracting colour and number of doors into seperate classes is too far, which I'd disagree with. Indeed, if car is a very simple car class, maybe it is somewhat over the top. However, there are only a certain number of valid colours, only certain numbers of doors are valid. What if I want to be able to identify my car colour by any of a series of aliases?
CarColor seems like a perfectly valid class to me. It can store all the logic of what is and is not a valid color, provide aliases etc.
Consider an example I've dealt with recently concerning names in a series of places in different forms:
Database 1: Joan Smith
Right, I'll take the name, stick it in a String and put it in my person. No point making a fuss and wrapping one thing in it's own class.
Database 2: Smith, Joan
Ok, so I'll override the setter, check for this format and flip it if it's right.
Database 3: person: { title: Mrs, first: Joan, last: Smith }
Right, we'll just stick the title on the beginning, concatenate the last 2 together and then glue the whole thing into our damned string.
Database 4: foreign name that doesn't follow your structure and the whole concept of first, middle, last takes on a completely different meaning.
...turns out names are complicated.
This can all be nicely avoided by taking your supposedly simple structures and delegating to another class to handle what a name means, when are two names the same, how do we print them etc.
Prematurely wrapping all singleton values in their own classes can be somewhat overkill - but I've found that a bias towards wrapping when in doubt tends to serve me well later even if it feels silly at the time, especially when the value permeates through the rest of the code and so changing it later affects everything.
Hi so I have a list of 'areas' that are just areas of my site. I want to be able to insert them in to the database and say what type of area they are, this could be the name of the model that inherits from a base Area. This way I can write a generic controller which uses different views and different logic from the loaded model to decide how the page should act.
Trouble is I have no idea how to do this as I'm new to rails.. Any pointers? (or anyone saying "don't do it like that! do it like this!" would also be much appreciated)
Thanks
I have just found Single Table Inheritance. This appears like it will solve my problem
for more information read here:
http://juixe.com/techknow/index.php/2006/06/03/rails-single-table-inheritance/
You'll want to create a column named :type
Rails will automatically use the type column to determine the class of the child class.
Then you can do something like:
class Area < ActiveRecord::Base
end
class UserPage < Area
end
So then when you do
UserPage.create( :key => 'value')
It will create an entry in your areas table with the type field set to UserPage.