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?
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.
I think that's what you are looking for?
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?
Yeah. It's helpful to so that you can save the correct type easily, although you don't have to.
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
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.
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.
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.
Join 29,763+ developers who get early access to new screencasts, articles, guides, updates, and more.