#[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