Is it bad practice to have two controllers for one model in Ruby on Rails? - ruby-on-rails

I have a model that gets treated differently by a parameter it holds, for example its like a character table, which also is used for non player characters, since they share all the same attributes. So it would have a Boolean or integer that would indicate if it was a player character or a non-player character. And non-player characters will be generated automatically with random status parameters and names.
Since the methods used for the two are radically different, I though it would be logical to have a different controller class for them, but that would make a single model have two different controllers, and feels somewhat odd.
Is this bad practice? Should I do all the coding in one controller?

It actually sounds to me like you should have two different models. If the way you interact with the objects is radically different, as you explained, then they really shouldn't be the same class. Ruby has a great way to deal with this case: Modules. You can use modules to create shared behaviors for objects. You can even store the non-player characters and characters in the same database table by overriding the ActiveRecord table name. For example:
module Character
def decrease_hitpoints x
...
end
# other shared functionality can go here
end
class PlayerCharacter < ActiveRecord::Base
set_table_name 'characters'
include Character
end
class NonPlayerCharacter < ActiveRecord::Base
set_table_name 'characters'
include Character
end
In this example both PlayerCharacter and NonPlayerCharacter share the same table name and functionality defined in Character, but they are different objects.
Finally, it's totally fine to use two or more controllers for a single model, just as it's fine to build a controller that doesn't depend on a model at all.

This is completely OK. What you are describing is a task-based user interface, where you are more interested in capturing a work flow or process, as opposed to a simple CRUD or resource operation. Remember, the default setups in Rails are meant to be very basic. Feel free to expand on the basics.

Related

Managing polymorphic data in Rails

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/

Can I use Single Table Inheritance with has_many to get a collection of multiple types

I have a Citation class and I want each Citation to have many Resources. A resource can be a LocalFile, Link (and maybe something like a dropbox file). Resources will share most functionality and columns. I want to use single table inheritance in the following manner.
class Resource < ActiveRecord::Base
belongs_to :citation
acts_as_list :scope => :citation
end
class LocalFile < Resource
end
class Link < Resource
end
class Citation < ActiveRecord::Base
has_many :resources, class_name: "Resource"
end
I want to be able to get a collection of resources with code something like
cites = mycite.citations
and have each object in cites have the appropriate type (LocalFile, Link etc..). Will this work? Strangely despite searching extensively I haven't been able to find an answer to this simple question.
Also, will the methods added by acts_as_list work appropriately when called on objects on the various subtypes, i.e., give move them up or down in the list on all resources for a single citation
Alternatively, is there a better way to do this? I want to have the Citation class have a collection of resources of multiple subtypes. Conceptually, each resource type is capable of representing the same content just accessed in different ways.
STI should be considered when dealing with model classes that share much of the same functionality and data fields, but you as the developer may want more granular control over extending or adding to each class individually. Rather than duplicate the code over and over for multiple tables (and not being DRY) or forego the flexibility of adding idiosyncratic functionality or methods, STI permits you to use keep your data in a single table while writing specialized functionality.
to implement STI:-
Create the base class
Along with your needed attributes, add a :type attribute as a string
to the base class
Create any necessary descendant classes
Drawbacks of STI
STI isn’t always the best design choice for your schema. If sub-classes that you intend to use for STI have many different data fields, then including them all in the same table would result in a lot of null values and make it difficult to scale over time. In this case, you may end up with so much code in your model sub-classes that the shared functionality between sub-classes is minimal and warrants separate tables. Also, as I mentioned above, STI shouldn’t be used just because two object types have similar attributes - both airplanes and bicycles have wheels, but it probalby doesn’t make sense to group them into the same table, given that intuitively, they’re different objects that will have vastly different functionality and data fields in an application.
HOPE IT HELPS :)

Single Responsibility Principle- when to stop extracting code into separate classes

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.

What's the convention in decoupling components in Rails

Using rails generate model, I created two models/tables, policeman and policewoman. They are lists of officers with many attributes (age, time in the field, case solved, etc.). If I wanted to do a computation that determines who should get the next promotion to Sargent what is the conventional way to do it in rails?
I would want to create a new class just to deal with this situation, and hide all the complex computation (compare attributes between both lists) away from the caller. So maybe in a controller, say Captain, under the method show I would do
class Captain < ApplicationController
def show
promotion = Promotion.new
#ideal_sargent = promotion.sargent(Policeman.find(:all),Policewoman.find(:all))
end
end
Where do I create this Promotion class? Do I use rails generate controller to make it? Or maybe make a gem for it? Or even put it all in a model (I hear thin controllers fat models)?
EDIT:
Maybe this? If so, how do I create a model without a migration file being automatically made?
Your general idea of decoupling it from models and from controllers is a good one. Although you confuse it a bit with controllers, generators and gems...
What you want to do is:
introduce a service object which is plain ruby object
put the logic for calculating promotion order inside it
have it completely decoupled from controller and loosely coupled to police officers models
The interface to use it would be basically as you have already described:
# prepare some officers to choose from
officers = [PoliceWoman.find(1)] + PoliceMan.all
# returns the first ranking officer for promotion
to_be_promoted = SargentPromotionService.new.find_top_candidate(*officers)
Where to put this service model? I suppose it contains application specific logic, that isn't really useful outside of application. Therefore we put it in the app folder. But where in the app?
A good practice is to setup the app/domain folder. There you can put all the app specific domain models (service, policy, value objects, etc...). All you need to setup this folder is add it to the autoload paths inside the config/application.rb:
config.autoload_paths += %W(#{config.root}/app/domain)
This way you have a clear separation of rails models (take care of persistence) and domain models - where you should put most of the application specific code. If your app is really simple and small you could also skip the app/domain folder and just use the app/models. And if you follow the approach to use plain ruby objects with loose coupling, you will get easily testable, maintainable, flexible and reusable code. =)
For this purpose, I wouldn't create two tables to modeling the data. I would use a single table named polices, and keep a column as gender and another one as rank to differ policeman and policewoman, and different rank. Then I would put promote function as a class method inside the Police modal.
class Police < ActiveRecord::Base
def self.promote(param1, param2)
....
end
end
In this way you can incapsulate the business logic inside the promote function, the caller can invoke it without knowing any complex computation in it. Police.promote(a,b)

Learning Basic rails: Where to put conditional business logic?

So Im trying to teach myself rails and having some trouble with figuring out where logic goes.
In my exercise, I have a payment model.
class pyament
Integer Product_Type
String Product_name
There are rules for handling payments
if the product_Type is physical, do this, if virtual do that
if the product_name is book, do this
if the product_name is cow, do that
What I cant figure out is where to put these rules.
Do I make a method in the model called process that runs these rules? Does this go logic go in the controller? Im just not clear on this.
Any insight would be appreciated.
Thanks
You should definitely keep this logic in the model, and in fact, if the logic is significantly different between different types you should use multiple models with Single Table Inheritance.
See:
http://joemcglynn.wordpress.com/2009/12/11/rails-sti-part-1/
http://joemcglynn.wordpress.com/2009/12/12/rails-single-table-inheritance-part-2/
Basically the idea is this: You're already defining Product Type -- the 'type' column is the main feature of an STI table.
With STI instead of having one model with tons and tons of conditional logic or multiple models, you have several VERY SIMILAR models with VERY SIMILAR data but somewhat different logic, so all those related models can share the same table, and inherit from a common class.
For instance:
class Product < ActiveRecord::Base
...
common logic goes here
...
end
class PhysicalProduct < Product
...
physical-product-specific logic goes here
...
end
class VirtualProduct < Product
...
virtual-product-specific logic goes here
...
end
So, in this way you can create a method like product.deliver which is defined by default in the product model to trigger shipping a product -- but in the VirtualProduct model is overridden to trigger emailing a download link instead.
ActiveRecord handles all of this very nicely (see the linked articles above for a walkthrough), and the majority of your forms and links and controllers etc. will continue to work the same way they currently do.
In general you always want to keep as much logic as possible in the models instead of the controllers, because models are easier to test, and easier to debug. In your situation STI is a nice way to keep this branching logic in the models an out of the controllers and views.
For starters, this list is a nice jumping off point.
I agree that the data manipulation / business rules best fit in the model. Keep the views and controllers clean so your model can contain all the storage structures and logical rules.
Generally you want your business logic in the models. "Fat Model, Skinny Controller" is the cool Rails phrase used to describe this. See this post for more info, and don't pay attention to the out-dated Rails syntax.

Resources