So I have the ActiveRecord model like this
class User < ActiveRecord::Base
has_many :posts
end
And I have Api class that fetches the attributes from web for User and returns a hash, which in turn needs some preprocessing to fit into the user model. E.g. the response from api is {response: {id:20, stars:{count:20}}} needs to be processed to user.id=20 and user.stars_count=20.
Now there are 2 ways that I can easily see to do the fecting thing. One way is add a method to Api like get_user, that does all the processing returns a new User model with filled attributes. The other is to add fetch method to User that does all the processing.
In the first case I don't like that my Api class will be filled with a lot of get_model methods like get_user, get_post, get_all_posts_for_user etc. and quickly can become unmanagable. In the second case the model is filled with a lot of preprocessing stuff methods like fetch, fetch_all_posts and looks nasty too.
So what is the best practice to deal with this problem?
Take a look at Active Resource, you don't have to do what you are doing manually.
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/
Let's say I have User model with a boolean flag called email_notifications. This lets us know whether this user cares to receive email notifications from the app.
Recently I've been moving all these flags and settings into a separate sub-model call Preference to keep it away from the core user model. A User has_one Preference.
class User < ActiveRecord::Base
has_one :preference, dependent: :destroy
accepts_nested_attributes_for :preference
scope :email_recipients, -> { where(preference: { email_notification: true} ) }
def can_receive_email?
preference.email_notification
end
end
As you can see, I have a helper scope and a helper method on the model.
However, now that it lives in another table, I have always query the association :preference first and then get the value I need. It's not directly available from the User model itself any more.
Is this considered bad practice? Or does Rails help out and cache associated models so it doesn't have to re-query every time? I like organizing my various configs in a separate model, which I DO think is good practice, but this potential cost of constantly re-querying is stopping me.
Thanks!
EDIT:
I'm aware that I can do User.includes(:preference).blah_blah, however I want to avoid updating this in EVERY place where I call the user, and it may not always be known ahead of time whether I'm going to query the sub-model and need to include an .includes()
Rails associations are stored in memory after they're accessed so calling user.preference wouldn't hit the database except for the first time it's referenced.
Also, includes wouldn't have much of an effect in this case since this is a has_one. includes is usually useful for eager loading many associations in a single larger query rather than hitting the database each time a child object is called.
My application needs to implement a functionality for performing competitions of photographers. Process looks like this: Administrator creates a competition, then user can apply a request for participating. Admin can approve request or it can deny it. Admin does it by creating a response with response status field assigned to ACCEPTED (0) or BANNED (100).
class Competition
end
class User
end
class CompetitionRequest
belongs_to :user
belongs_to: competition
end
class CompetitionResponse
ACCEPTED = 0
BANNED = 100
belongs_to :competition_request
end
class Photo
belongs_to :competition
end
But later i need to figure out how to filter banned photos from showing them to users and jury.
And Photo has no direct connection to CompetitionResponse. Photo and CompetitionResponse have relations to Competition but i can not figure out how to make filtration possible. I tried doing it via plain sql (find_by_sql) inside Photo model (it returns records only if appropriate request was approved) but it does not seems to be a good design, because i reference value defined in CompetitionResponse class in Photo model.
Later i changed design and make a before_save filter inside CompetitionResponse model, it checks if status == BANNED and if true, marks all the photos posted to competition by user (who's request was banned) as banned (i added a status field to Photo model).
But now i feel it looks not good. CompetitionRequest model takes too much responsibility.
What is the best way to design it ? May be there must some kind of Observer or something like this or another design pattern ?
First step I would take is assign each Photo to the user it "belongs_to". This will allow you to, at any time, check whether the "owning" user was indeed accepted or banned for the competition that photo refers to...
It seems to me that you should have a many-to-many relation between Competition and User. When the admin accepts a user into the competition, you associate the records. This keeps things simple in your data model and also allows for cases where Users get into a Competition without requesting it (perhaps as part of a tournament?).
Also consider getting rid of CompetitionResponse - it seems like this should just be an attribute of CompetitionRequest, as there would only be one response to a request, right?
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 read everywhere that business logic belongs in the models and not in controller but where is the limit?
I am toying with a personnal accounting application.
Account
Entry
Operation
When creating an operation it is only valid if the corresponding entries are created and linked to accounts so that the operation is balanced for exemple buy a 6-pack :
o=Operation.new({:description=>"b33r", :user=>current_user, :date=>"2008/09/15"})
o.entries.build({:account_id=>1, :amount=>15})
o.valid? #=>false
o.entries.build({:account_id=>2, :amount=>-15})
o.valid? #=>true
Now the form shown to the user in the case of basic operations is simplified to hide away the entries details, the accounts are selected among 5 default by the kind of operation requested by the user (intialise account -> equity to accout, spend assets->expenses, earn revenues->assets, borrow liabilities->assets, pay debt assets->liabilities ...) I want the entries created from default values.
I also want to be able to create more complex operations (more than 2 entries). For this second use case I will have a different form where the additional complexity is exposed.This second use case prevents me from including a debit and credit field on the Operation and getting rid of the Entry link.
Which is the best form ? Using the above code in a SimpleOperationController as I do for the moment, or defining a new method on the Operation class so I can call Operation.new_simple_operation(params[:operation])
Isn't it breaking the separation of concerns to actually create and manipulate Entry objects from the Operation class ?
I am not looking for advice on my twisted accounting principles :)
edit -- It seems I didn't express myself too clearly.
I am not so concerned about the validation. I am more concerned about where the creation logic code should go :
assuming the operation on the controller is called spend, when using spend, the params hash would contain : amount, date, description. Debit and credit accounts would be derived from the action which is called, but then I have to create all the objects. Would it be better to have
#error and transaction handling is left out for the sake of clarity
def spend
amount=params[:operation].delete(:amount)#remove non existent Operation attribute
op=Operation.new(params[:operation])
#select accounts in some way
...
#build entries
op.entries.build(...)
op.entries.build(...)
op.save
end
or to create a method on Operation that would make the above look like
def spend
op=Operation.new_simple_operation(params)
op.save
end
this definitely give a much thinner controller and a fatter model, but then the model will create and store instances of other models which is where my problem is.
but then the model will create and store instances of other models which is where my problem is.
What is wrong with this?
If your 'business logic' states that an Operation must have a valid set of Entries, then surely there is nothing wrong for the Operation class to know about, and deal with your Entry objects.
You'll only get problems if you take this too far, and have your models manipulating things they don't need to know about, like an EntryHtmlFormBuilder or whatever :-)
Virtual Attributes (more info here and here) will help with this greatly. Passing the whole params back to the model keeps things simple in the controller. This will allow you to dynamically build your form and easily build the entries objects.
class Operation
has_many :entries
def entry_attributes=(entry_attributes)
entry_attributes.each do |entry|
entries.build(entry)
end
end
end
class OperationController < ApplicationController
def create
#operation = Operation.new(params[:opertaion])
if #operation.save
flash[:notice] = "Successfully saved operation."
redirect_to operations_path
else
render :action => 'new'
end
end
end
The save will fail if everything isn't valid. Which brings us to validation. Because each Entry stands alone and you need to check all entries at "creation" you should probably override validate in Operation:
class Operation
# methods from above
protected
def validate
total = 0
entries.each { |e| t += e.amount }
errors.add("entries", "unbalanced transfers") unless total == 0
end
end
Now you will get an error message telling the user that the amounts are off and they should fix the problem. You can get really fancy here and add a lot of value by being specific about the problem, like tell them how much they are off.
It's easier to think in terms of each entity validating itself, and entities which depend on one another delegating their state to the state of their associated entries. In your case, for instance:
class Operation < ActiveRecord::Base
has_many :entries
validates_associated :entries
end
validates_associated will check whether each associated entity is valid (in this case, all entries should if the operation is to be valid).
It is very tempting to try to validate entire hierarchies of models as a whole, but as you said, the place where that would be most easily done is the controller, which should act more as a router of requests and responses than in dealing with business logic.
The way I look at it is that the controller should reflect the end-user view and translate requests into model operations and reponses while also doing formatting. In your case there are 2 kinds of operations that represent simple operations with a default account/entry, and more complex operations that have user selected entries and accounts. The forms should reflect the user view (2 forms with different fields), and there should be 2 actions in the controller to match. The controller however should have no logic relating to how the data is manipulated, only how to receive and respond. I would have class methods on the Operation class that take in the proper data from the forms and creates one or more object as needed, or place those class methods on a support class that is not an AR model, but has business logic that crosses model boundaries. The advantage of the separate utility class is that it keeps each model focused on one purpose, the down side is that the utility classes have no defined place to live. I put them in lib/ but Rails does not specify a place for model helpers as such.
If you are concerned about embedding this logic into any particular model, why not put them into an observer class, that will keep the logic for your creation of the associated items separate from the classes being observed.