Batman.js: Related model undefined for polymorphic association not found. Same namespace - polymorphic-associations

I am trying to set up the polymorphic association in Batman as documented here. I am using LocalStorage and keep getting the following message:
Related model undefined for polymorphic association not found.
My model is a follows:
class App1.Model extends Batman.Model
#persist Batman.LocalStorage
class App1.Superpower extends App1.Model
#resourceName: 'superpower'
#encode 'id', 'name'
#belongsTo 'superpowerable', polymorphic: true
class App1.Hero extends App1.Model
#resourceName: 'hero'
#encode 'id', 'name'
#hasMany 'superpowers', as: 'superpowerable', polymorphic: true
class App1.Villain extends App1.Model
#resourceName: 'villain'
#encode 'id', 'name'
#hasMany 'superpowers', as: 'superpowerable', polymorphic: true
and the code I use to instantiate all:
superman = new App1.Hero(name: "Superman")
superman.save()
super_strength = new App1.Superpower(name: "Super Strength")
super_strength.save() # -> gives "Related model undefined for polymorphic association not found."
invincibility = new App1.Superpower(name: "Invincibility")
invincibility.save() # -> gives "Related model undefined for polymorphic association not found."
superman.get('superpowers').add super_strength
superman.get('superpowers').add invincibility
superman.save()
super_strength.save()
invincibility.save()
console.log superman.toJSON()
console.log super_strength.toJSON()
console.log invincibility.toJSON()
According to this question it depends on the namespace, but they are all the same in my code, so I'm really wondering what is going wrong here.
Thanks in advance

Uh oh... I wrote that example so if it doesn't work, I'm to blame :)
Just to be sure, did you call App1.run() at the beginning of that script? I ran into that issue while testing last week -- gotta call run to load associations!
Edit:
Oh, it looks like a matter of using the API just right. You have to be explicit with the #{label}_type and #{label}_id of the related models. Batman.js will load the associations from JSON just fine, but you'll have to specify them when initializing a new record, for example:
super_strength = new App1.Superpower
name: "Super Strength"
superpowerable_id: superman.get('id'),
superpowerable_type: 'Hero'
I put up a JSFiddle where it's working: http://jsfiddle.net/2atLZ/2/
I'll go back to the docs and add a note about this! Long term, would be great if the API just accepted #{label} then extracted #{label}_id and #{label}_type... but for now, it's not so.!

Related

Rails accept_nested_attributes creating a new resource with specific id

I've got an incoming SOAP request that tries to create or update nested resources. The incoming resources already have an ID that I'd like to use as my own, instead of generating a new one. Before I go any further, let me provide you with some context.
I've got the following models:
class AccountedTime < ApplicationRecord
belongs_to :article, optional: false
end
class Article < ApplicationRecord
has_many :accounted_times, dependent: :destroy
accepts_nested_attributes_for :accounted_times, allow_destroy: true
end
The problem that I have occurs when creating new resources, so assume the database is completely blank at the start of every code block. I already transformed the incoming data so it matches the Rails format.
# transformed incoming data
data = {
id: 1234,
title: '...',
body: '...',
accounted_times_attributes: [{
id: 12345,
units: '1.3'.to_d,
type: 'work',
billable: true,
}, {
id: 12346,
units: '0.2'.to_d,
type: 'travel',
billable: false,
}],
}
When creating a non-nested resource you can provide an id (assuming it's not taken) and it will be saved with the provided id.
article = Article.find_or_initialize_by(id: data[:id])
article.update(data.except(:accounted_times_attributes))
# The above will save and create a record with id 1234.
However the above doesn't work for nested resources when using the accept nested attributes interface for the update method.
article = Article.find_or_initialize_by(id: data[:id])
article.update(data)
ActiveRecord::RecordNotFound: Couldn't find AccountedTime with ID=12345 for Article with ID=1234
The result I'm trying to achieve is to create the resources with their provided id. I thought I might have overlooked an option to toggle some sort of find_or_initialize_by or find_or_create_by mode when providing nested attributes. Am I missing something here?
I'm currently using the following alternative (without error handling for simplicity):
article = Article.find_or_initialize_by(id: data[:id])
article.update(data.except(:accounted_times_attributes))
data[:accounted_times_attributes]&.each do |attrs|
accounted_time = article.accounted_times.find_or_initialize_by(id: attrs[:id])
accounted_time.update(attrs)
end
I think that your problem is not accepts_nested_attributes_for configuration, which seems to be ok.
Maybe your problem is because you are using a reserved word for the Time model. Try to change the name of the model and test it again.
UPDATE:
That seems pretty weird, but looking for the same problem I found this answer that explains a possible reason for the error in Rails 3 and show two possible solutions. Feel free to check it out, hope it helps.

Manually adding data to a namespaced model in rails

I'm trying to manually add data to a table in rails using the rails console, but I keep getting a undefined local variable or method error.
The model is namespaced under ratings:
models/ratings/value_for_money_score.rb
class Ratings::ValueForMoneyScore < ApplicationRecord
end
To try and manually add a record to the database, I run the following in the console:
$ RatingsValueForMoneyScore.create(score: '1', description:"Terrible, definitely not worth what was paid!")
And I get this error: NameError: uninitialized constant RatingsValueForMoneyScore
I have tried a few different versions such as
RatingsValueForMoneyScores.create,
Ratings_ValueForMoneyScores.create,
ratings_value_for_money_scores.create but keep getting the same error. What am I doing wrong?
Try
::ValueForMoneyScore.create(score: '1', description:"Terrible, definitely not worth what was paid!")
The error is descriptive enough in this case, the class RatingsValueForMoneyScore pretty much doesn't exist. Check your namespace, you have a class name (which should be singular) Rating, and the module ValueForMoneyScore. You'd use either
(after renaming the class to Rating)
Rating.create(...)
or
ValueForMoneyScores.create(...)
Your syntax is equivalent to:
class Rating
module ValueForMoneyScores < ApplicationRecord
...
end
end
The answer was a combination of input, so collecting that in one answer.
My namespaces weren't properly set up (I was using "Ratings" rather than "Rating"), so I fixed this for the tables. My models then looked as follows:
models/rating.rb
class Rating < ApplicationRecord
end
models/rating/value_for_money_score.rb
class Rating::ValueForMoneyScore < ApplicationRecord
end
And then this command worked for creating records in the rating_value_for_money_score table
$ Rating::ValueForMoneyScore.create(score: '1', description: "Test")

Rails Joins through a module

I'm trying to do a pretty simple join in my model to list all 'Locations' in a 'Post' with a certain id.
Currently, each post has_many :locations, :through => :location_post. I'm using the 'blogit' gem, which puts posts in a module named 'Blogit::Posts'.
I'm getting a wrong argument type Class (expected Module) error when I try to run the following in my Post.rb model:
module Blogit
class Post < ActiveRecord::Base
before_save :reuse_existing_locations
def reuse_existing_locations
existing_locations = Location.include(Blogit::Post).first
end
How can I do a join through a module?
Thanks in advance!
I'm not sure I understand what you're trying to accomplish so just some notes and observations:
By looking at the code, it's clear that Blogit::Post is a class, not a module.
The include method takes modules (not classes), that's the error you're seeing.
You are calling the include method on the Location model and that seems kind
of strange to me. Did you mean to call includes? But then again that
wouldn't make much sense since it seems like you've got a many to many
relationship between Location and Blogit::Post.
In the Location model (which doesn't need to be in the Blogit namespace), you can simply reference the Blogit::Post model as
follows:
has_many :posts, class_name: "Blogit::Post", ...
If existing_locations is in fact an attribute on the model and you want to assign to it, you need to put self in front of it (as in self.existing_locations). Otherwise you're just creating a local variable.
You probably wanted to use ActiveModels includes instead of Rubys include, which is to include methods from another module.

Batman.js: Related model undefined for polymorphic association not found

I'm running into an issue trying to implement polymorphic associations on a Batman model. I'm getting this error in the console:
Related model undefined for polymorphic association not found.
I'm having a hard time tracking down where I am going wrong. Where should I look to find the missing piece?
My models look something like this:
class Admin.Product.PopularCollectables extends Batman.Model
#belongsTo 'collectable', polymorphic: true
class Admin.Item extends Batman.Model
#hasOne 'popular_collectable', as: 'collectable'
When Batman loads a related model in an association, it checks a namespace. By default, the namespace is Batman.currentApp (which is your app after you call MyApp.run()), but you can also pass a namespace when you declare the association:
class Admin.Item extends Batman.Model
#hasOne 'popular_collectable', as: 'collectable', namespace: Admin.Product
That way, Batman will look for PopularCollectable on Admin.Product instead of on Admin.
I was able to workaround this issue by embedding the belongs_to side of a has_many relation inside the parent Batman Model instance when it is sent to the client from Rails:
format.json {render :json => #post, :include => {:comments => {:include => :comments}}}

Polymorphic association foreign_type sets ancestor type instead current, using STI class

I have
class Car < ActiveRecord::Base; end
class Porsche < Car
has_many :page_items, :as=>:itemable, :dependent=>:destroy
end
I have to mention that I'm using a single table called cars which has a field type.
class PageItem<ActiveRecord::Base
belongs_to :itemable, :polymorphic=>true
end
When I do
a = PageItem.new
a.itemable = Porsche.new
a.itemable
#<Porsche id: nil, type: "Porsche", name: nil, ..etc>
a.itemable_type
=> "Car"
And it should be
a.itemable_type
=> "Porsche"
Do anybody have an idea about this?
Update
According to bor1s's answer probably this is the right behaviour. So if is it, then my question is how can I set it to Porsche implicitly?
This is because Porshe is virtual model, it is not really exist in in database, it just has type field to know that it is Porshe. Maybe you should get rid of STI and use say type column to save Car model with type of Porshe and use scope to find porshes. I hope I helped you alittle.
I think if you add type field to the Car table, you'll be able to get Porsche type using :
PageItem.new(:itemable => Porsche.new).itemable.type

Resources