Open sfcgeorge opened 6 years ago
Well I came to the conclusion that it's not easy to get saving of HABTM working. Because it translates to has_many :through which Hyperloop doesn't support saving to. But even if it did, there is no server-side join model, so Rails complains.
For now at least, I've decided the best thing to do is leave my HABTM patch for read-only associations, but for associations that need writing, convert them to has_many through on the server side. It's not a difficult conversion, and allows you to add validations which might otherwise have been on the parent but work better with hyperloop on the join. So after manual conversion, this is how it looks:
What you start with, HABTM
class Post < ApplicationRecord
has_and_belongs_to_many :tags
end
class Tag < ApplicationRecord
has_and_belongs_to_many :posts
end
# what works with my patch in a component
render(DIV) do
Post.all.first.tags.each do |tag|
SPAN { tag.name } # yay
end
end
# but saving doesn't work
render(DIV) do
# no execute_remote, not saved
Post.all.first.tags << Tag.find_by(name: "Hyperloop")
end
How you refactor to has_many through
class Post < ApplicationRecord
has_many :posts_tags
has_many :tags, through: :posts_tags
end
# * the order of the class names must be alphabetical.
# * yes the first is plural and second is not.
class PostsTag < ApplicationRecord
belongs_to :post
belongs_to :tag
# optional validation example, bonus of manual conversion
validate :maximum_comments
private
def maximum_comments
return unless post.comments.count > 10
errors.add(:comments, "maximum 10 comments, stop spammin!")
end
end
class Tag < ApplicationRecord
has_many :posts_tags
has_many :posts, through: :posts_tags
end
# This listing example above still works
# This is what you want to do but still doesn't work
Post.all.first.tags << Tag.find_by(name: "Hyperloop")
# This also doesn't work but feels like it should
Post.all.first.posts_tags.create tag: Tag.find_by(name: "Hyperloop")
# This does work but feels gross. Permission works nicely at least
PostsTag.create(post: Post.all.first, tag: Tag.find_by(name: "Hyperloop"))
This code implements
has_and_belongs_to_many
in Hyperloop by using the existinghas_many :through
functionality and dynamically generating the join table.Note that this patch alone only enables read only access. This is because Hyperloop doesn't support writing to
has_many :through
e.g. with the<<
method (but Rails does). You can't work around it by trying to create the join table directly because it doesn't exist on the server side so execute_remote fails.The
inverse_of
isn't necessary but can't hurt I think. I hoped it would make the association writeable but it doesn't.Needs tests adding to test_app.
Tested in lap17 and Opal 0.10