Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dealing with multiple-inverse associations #57

Closed
digitalcora opened this issue Jan 20, 2015 · 10 comments
Closed

Dealing with multiple-inverse associations #57

digitalcora opened this issue Jan 20, 2015 · 10 comments

Comments

@digitalcora
Copy link

Consider the following data model:

Location = DS.Model.extend
  name: DS.attr 'string'
  currentFloorplan: DS.belongsTo 'floorplan', async: true
  upcomingFloorplans: DS.hasMany 'floorplan', async: true

Floorplan = DS.Model.extend
  name: DS.attr 'string'
  location: DS.belongsTo 'location', async: true, inverse: null

A Floorplan always belongs to a Location, but the inverse association could be one of two different options, so we set it to null because Ember only allows a single inverse. Unfortunately there's no clean way to deal with this situation in Factory Guy, so we end up doing this Em.run workaround in every test that involves floorplans:

describe 'An association with multiple possible inverses', ->
  it 'requires a clunky workaround', ->
    location = factories.make 'location'
    floorplan = factories.make 'floorplan', location: location
    Em.run -> location.get('upcomingFloorplans').addObject(floorplan)

    expect(location.get('upcomingFloorplans.length')).to.equal 1

In Factory Girl, this kind of thing is usually handled with callbacks that access transient attributes to execute some custom behavior. It would of course be a substantial effort to add these features to Factory Guy, so maybe there is an easier targeted solution.

@danielspaniel
Copy link
Collaborator

@Grantovich , how could you see this working in FactoryGuy? Could you give me an idea of what you would like to see ( even if its just a copy of what FactoryGirl does ) .. will help to get my ideas going.

@danielspaniel
Copy link
Collaborator

@Grantovich .. I just looked over the callbacks in FactoryGirl, and I understand what you want .. I just want to make double extra sure that I have it 100% exactly correct ( the way you want it ) before I build this.

@digitalcora
Copy link
Author

If you're looking to implement the whole transients/callbacks thing, this is roughly how I'd envision it working in the factory definitions:

FactoryGuy.define 'location',
  default:
    name: 'Test Location'

FactoryGuy.define 'floorplan',
  default:
    location: {}
    name: 'Test Floorplan'

  transient:
    locationInverse: null

  afterMake: (model, attributes) ->
    switch attributes.locationInverse
      when 'currentFloorplan' then model.location.set('currentFloorplan', model)
      when 'upcomingFloorplans' then model.location.get('upcomingFloorplans').addObject(model)

And the usage:

location = factories.make 'location'
floorplan = factories.make 'floorplan', location: location, locationInverse: 'upcomingFloorplans'

The transient key would contain attributes that are ignored when constructing the model itself. The afterMake callback would receive the constructed model instance and the hash of attributes provided to the make call, including any transient attributes.

If implemented as-is, this would not be backwards compatible since transient and afterMake could previously be used as trait names. Since this problem would keep showing up if you added more "special" keys, it might be worthwhile to put traits under their own sub-key to ensure there are no collisions.

FactoryGuy.define 'user',
  default:
    username: 'bob'
    isModerator: false
    isAdmin: false

  traits:
    admin:
      isAdmin: true
    moderator:
      isModerator: true

This would be super-not-backwards-compatible, but you could leave in the ability to define traits at the top level and just give a deprecation warning.

@digitalcora
Copy link
Author

(By the way, I and my team really appreciate your work on this project – I was only asking for advice on how to handle our particular situation, but you've gone far above and beyond. 👏 We are on a rather time-crunched project right now, but I hope to make some contributions to Factory Guy myself once I have more Ember experience.)

@danielspaniel
Copy link
Collaborator

Thanks for the proposed usage Alex, that was helpful, and thanks for the kind words :)
This library has made testing ember app's so much easier and fun for me, and I am happy to hear that others like yourselves are enjoying it too.
And good point about the collisions .. luckily traits already have their own sub_key already,
so that should not be too bad.

@digitalcora
Copy link
Author

luckily traits already have their own sub_key already, so that should not be too bad.

Ah, okay. I was looking at a section of the README that just used top-level keys for the traits.

@danielspaniel
Copy link
Collaborator

Hey @Grantovich , I finally got around to finishing this feature. Check it out in version 1.0.5, and let me know if you see any bugs?

@digitalcora
Copy link
Author

Awesome! Thanks so much for adding this, I'll try it out and report any issues I find.

@danielspaniel
Copy link
Collaborator

Actually I was not quite finished when I pushed out version 1.0.4 .. I am finishing it up today, and will release new version 1.0.5 when I am done.

@danielspaniel danielspaniel reopened this Apr 27, 2015
@danielspaniel
Copy link
Collaborator

ok .. I fixed that issue, and its on version 1.0.6 where afterMake will work as you expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants