Search code examples
ember.jsactive-model-serializers

How do I properly set up a single-table inheritance structure using Ember's Active Model Adapter and Rails' Active Model Serializer?


I'm creating a board game with a human player vs. a bot player, both types stored on a players table with a type column. Each player has their own board.

I have a Rails API using the active model serializer gem. My ember app connects to it using active model adapter.

Here are my serializers:

class GameSerializer < ActiveModel::Serializer
    attributes :id, :winner_id, :created_at, :updated_at
    has_many :players, polymorphic: true, serializer: PlayerSerializer
end

class PlayerSerializer < ActiveModel::Serializer
    ### Players can be type Human or Bot  
    attributes :id, :name, :game_id, :type, :created_at, :updated_at
    has_one :board
end

class BoardSerializer < ActiveModel::Serializer
    attributes :id, :player_id, :created_at, :updated_at
    has_one :player, polymorphic: true
    has_many :pieces
end

And here are my Ember models:

// game.js
import DS from 'ember-data';

export default DS.Model.extend({
    players: DS.hasMany('players', {async: true, polymorphic: true}),
    human: DS.belongsTo('human', {async: true}),
    bot: DS.belongsTo('bot', {async: true})
});

// player.js
import DS from 'ember-data';

export default DS.Model.extend({
    game_id: DS.attr(),
    name: DS.attr(),
    game: DS.belongsTo('game', {async: true }),
    board: DS.belongsTo('board', {async: true})
});

// human.js
import DS from 'ember-data';
import Player from './player';

export default Player.extend({
    type: DS.attr('string', {defaultValue: 'Human'})
});

// bot.js
import DS from 'ember-data';
import Player from './player';

export default Player.extend({
    type: DS.attr('string', {defaultValue: 'Bot'})
});

// board.js
import DS from 'ember-data'
export default DS.Model.extend({
    player_id: DS.attr(),
    player: DS.belongsTo('player', { async: true, polymorphic: true }),
    pieces: DS.hasMany('pieces', { async: true })
})

On the games.show route, I want to be able to call something like game.get('human.board.pieces') and game.get('bot.board.pieces') but no matter what I try, game.get('human.board.id') always returns undefined. I've also tried retrieving the board separately from the store, but the player_id is on the board, not the other way around.

Can anyone can help me figure out how to configure my AMS serializers and Ember Models correctly?


Solution

  • I ended up treating humans and bots as their own models since I would never really call game.get('players') this in the Ember app and it worked. Didn't have to touch the player, human, bot, or board models.

    // game.js
    export default DS.Model.extend({
        human: DS.belongsTo('human', {async: true}),
        bot: DS.belongsTo('bot', {async: true})
    });