Ask A Question

Notifications

You’re not receiving notifications from this thread.

Can I 'alias' a Rails active model to another class

Dan Tappin asked in General

First off I am sure I am not asking this question correctly as this is an advanced Rails topic over my head right now. Here is what I have for routes:

concern :assetable do
resources :assets, concerns: [:workable, :trackable, :flagged, :historical, :commentable, :uploadable], shallow: true do
resources :assets, path: :components, as: :components, shallow: true#, only: [:index, :create, :new]
end
end  

and my asset model:

has_many :assets, :as => :assetable, :dependent => :destroy
alias_attribute :components, :assets

My premise here is I have an Asset model that can polymorphicly belong to pretty much any other model in my Rails app - including another Asset. My my case I am limiting this case to one level deep and calling these Components. This is like having a car as an asset and it has an engine, transmission etc. as a component. For my purposes it's simple enough to make a common set of attributes that are shared between both the assets and components,

Now I could just create an entire new model but the entire data structure, views controllers etc are for the most part the same so I figured that was just a waste of time and makes it a mess to keep this code in sync.

As I go along now I am having to add some logic to my views to ensure the headings i.e. "Assets" vs. "Components" and I have a simple instance variable in my controller @asset_class which returns 'asset' or 'component'. The ugly part now is when I start working with routes. All the awesome Rails url helpers etc. fail me here because if I call url_for( ) on a component record I get /assets/etc/etc when I really want /components/etc/etc/. I essentially want to Assets nested under Assets to be represented as Component class not Asset.

Can I do this?

Reply

I'm not sure I totally understand, but you might consider creating a Component class that inherits from Asset so that you can store all those in the assets table but treat them differently. This is generally called single table inheritance.

http://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html

I think that's what you are looking for?

Reply

Thanks for the tip Chris. I think that s what I am looking for. So from what I gather I add a 'type' column and Rails pretty much handles the rest. Do you make a separate controller too?

Reply

Yeah. It's helpful to so that you can save the correct type easily, although you don't have to.

Reply

If you want some free rep over at SO cross post your answer there too :) http://stackoverflow.com/questions/37122490/can-i-alias-a-rails-active-model-to-another-class

Reply

Ha! I'll take it. :)

Reply

So let me get this straight then - I would add that inherited class, add the 'type' column and then add that as a normal nested resource under assets. If I post a new 'component' to the new_component path etc. the Asset controller still handles this but the 'type' would not get set. This is why I would need my own controller.

My first thought is keep the single controller and just add some logic to call 'Asset.create...' or 'Component.create...' etc. as required as it should be the same for both.

Reply

You could do just one controller and then just make sure to pass in the type into the URL or the form as a hidden field. Then you could use that logic to create an Asset or a Component.

Reply

That was awesome. I have some small logic to still sort out. Just a few points: you need to create a Components controller and inherit it from the Assets (unless you want your own). Same goes for Pundit policies but in my case I had to create a separate scope anyway. The controller logic is easy - you can call controller_name and set the Asset class for each type. The only thing that is a bit of a gotcha is the model associations. Now that it's a direct has_many and not polymorphic I found i need to set the type field in the create section of the controller. Also Asset.all will return both Assets and Components while Components.all only Components. Adding a default scope / Pundit policy_scope fixes that. All the routes now just handle them selves and in the views I have a now just a small bit of logic (that still needs for refactoring) to handle the asset / component conditions.

Reply

Sounds like it turned out really smoothly! :D

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 87,400+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.