#[1]gnikyt feed [2]gnikyt Code ramblings. Polymorphic and Route Concerns... who is who? /* Jan 01, 2016 — 5.1KB */ The goal of this post is to outline some tips on easily figuring out the parent object for a polymorphic modal/route/controller. Let’s start with the basics… Polymorphic For those unfamiliar to it, [3]Polymorphic is an Active Record association type where a model can belong to other models. As a simple example, you could have an Ingredient model which can be polymorphic and belong to different types of models such as Baking, Cooking, or WitchesBrew. Route Concerns These are used in routing for Rails where you’re able to declare common routes for resources. An example of this can be a picture concern, where many resources can have a picture route. concern :picturable do resources :pictures end # ... resources :users, concerns: [:picturable] resources :customers, concerns: [:picturable] The problem If your polymorphic modal has a controller, how do you know what object is using it? How do you get the object itself? Let’s start and assume I have a polymorphic modal for Metafields, so many models can have metafields and we’ll call it fieldable. # modals/metafield.rb module MyCoolApp class Metafield < ActiveRecord::Base belongs_to :fieldable, polymorphic: true end end # modals/user.rb module MyCoolApp class User < ActiveRecord::Base # ... has_many :metafields, as: :fieldable, dependent: :destroy # ... end end # modals/movie.rb module MyCoolApp class Movie < ActiveRecord::Base # ... has_many :metafields, as: :fieldable, dependent: :destroy # ... end end So now, we have three models. The Metafield modal which is polymorphic and a User and a Movie modal which can have these metafields. The Metafield modal will create a table in the database with fieldable_type and fieldable_id which should reference the modal class and the object’s ID. Along with this, I’ve set up a Metafield controller so we can add, edit, and delete metafields for these other models. With all this put together, we’ll set up the routing concerns. concern(:fieldable) { resources :metafields } # ... resources :users do concerns :fieldable end # ... resouces :movies do concerns :fieldable end Now, the user and movie resource routes will have metafield resource routes added to them. Which will create routes such as /users/metafields, /users/metafields/new, /movies/metafields/3/edit. However, for the metafield controller, how is it supposed to know if we’re accessing User metafields or Movie metafields when you’re adding and editing? You could do things such as base it on the URL, or manual section, but that’s not a great solution in the long run. There are easier and cleaner ways… by utilizing a mix of the routing concerns and a private method in the Metafield controller. Let’s change our concern in the routing now to accept options and parameters. # Before concern(:fieldable) { resources :metafields } # After concern(:fieldable) {|opts| resources :metafields, opts} Now let’s pass a parameter to the concern per resource route. concern(:fieldable) {|opts| resources :metafields, opts} # ... resources :users do concerns :fieldable, fieldable_type: "MyCoolApp::Users" end # ... resouces :movies do concerns :fieldable, fieldable_type: "MyCoolApp::Movies" end So now we’re passing fieldable_type with the modal class to the concern which gets passed to the resource for metafields. We can now grab this parameter in the controller and it’ll help us figure out what modal is trying to access the metafields. Let’s add a method to the metafield controller now which will do this work for us. module MyCoolApp class MetafieldsController < ApplicationController before_action :set_object # ... private def set_object # Converts (as example) "MyCoolApp::Movies" string to "movies_id" param_name = "#{params[:fieldable_type].demodulize.underscore}_id" # Converts (as example) "MyCoolApp::Movies" string into a module referen ce param_object = params[:fieldable_type].constantize # Grab the object now, as example: (object.find movie_id) -> MyCoolApp:: Movies.find 3 @object = param_object.find params[param_name] end end end As you can see above, everything is now in place. We convert the fieldable_type value we passed in the concern into a module reference and an ID for whose trying to access it. @object will not be the User object or Movie object trying to access the metafields. Lastly, we can tie this into the forms for metafields creation/editing: # ...
<%= f.text_field :fieldable_id, value: @object.id %> <%= f.text_field :fieldable_type, value: @object.class %>
Now when saved, the metafield record in the database will automatically set the modal class and the ID for the object. [4]MD | [5]TXT | [6]CC-4.0 This post is 9 years old and may contain outdated information. __________________________________________________________________ [7]Ty King Ty King A self-taught, seasoned, and versatile developer from Newfoundland. Crafting innovative solutions with care and expertise. See more [8]about me. [9]Github [10]LinkedIn [11]CV [12]RSS * * * * * * * * * * References Visible links: 1. /rss.xml 2. / 3. http://guides.rubyonrails.org/association_basics.html#polymorphic-associations 4. /polymorphic-and-route-concerns/index.md 5. /polymorphic-and-route-concerns/index.txt 6. https://creativecommons.org/licenses/by/4.0/ 7. /about 8. /about 9. https://github.com/gnikyt 10. https://linkedin.com/in/gnikyt 11. /assets/files/cv.pdf 12. /rss.xml Hidden links: 14. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-1 15. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-2 16. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-3 17. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-4 18. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-5 19. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb1-6 20. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-1 21. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-2 22. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-3 23. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-4 24. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-5 25. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb2-6 26. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-1 27. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-2 28. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-3 29. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-4 30. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-5 31. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-6 32. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-7 33. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb3-8 34. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-1 35. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-2 36. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-3 37. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-4 38. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-5 39. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-6 40. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-7 41. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb4-8 42. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-1 43. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-2 44. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-3 45. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-4 46. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-5 47. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-6 48. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-7 49. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-8 50. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb5-9 51. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb6-1 52. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb6-2 53. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb6-3 54. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb6-4 55. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-1 56. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-2 57. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-3 58. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-4 59. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-5 60. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-6 61. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-7 62. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-8 63. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb7-9 64. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-1 65. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-2 66. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-3 67. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-4 68. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-5 69. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-6 70. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-7 71. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-8 72. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-9 73. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-10 74. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-11 75. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-12 76. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-13 77. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-14 78. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-15 79. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-16 80. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-17 81. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb8-18 82. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb9-1 83. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb9-2 84. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb9-3 85. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb9-4 86. localhost/tmp/lynxXXXXEjuZMX/L769284-9675TMP.html#cb9-5