mattkrick / cashay

:moneybag: Relay for the rest of us :moneybag:
MIT License
453 stars 28 forks source link

Integrating with Ember #120

Open dustinfarris opened 8 years ago

dustinfarris commented 8 years ago

This is a placeholder issue for me (and anyone else interested) to work on integrating Cashay with Ember.

This is my current thinking (TODO list):

It may be desirable to create an Ember addon to provide tooling for the above steps.

Currently cashay and graphql have to be imported via browserify.

dustinfarris commented 7 years ago

Quick update:

dustinfarris commented 7 years ago

broccoli plugin is finished and working: https://github.com/dustinfarris/broccoli-cashay-schema

I am almost done with ember-cashay, an Ember addon that will make using cashay a one-liner at the command prompt. Hope to release tomorrow. It is working, just finishing up documentation and tests.

mattkrick commented 7 years ago

whoa, that's amazing!

dustinfarris commented 7 years ago

ember-cashay is now on GitHub: https://github.com/dustinfarris/ember-cashay

This initial implementation proves that cashay and ember can work together (and remarkably well, I might add).

Working now:

Remaining before v0.1.0 release:

To see this in action, check out the dummy app ("dummy" apps are internal apps created for and used by Ember addons to run acceptance tests against) in the repo.

For example, here is a container component making a query: https://github.com/dustinfarris/ember-cashay/blob/master/tests/dummy/app/components/users-list.js

mattkrick commented 7 years ago

Whoa, this is huge! If you got an example app I'd love to check it out! The caahay-sandbox is crazy out of date, I'm hoping to find the time to showcase sockets in it soon

On Oct 31, 2016 1:04 PM, "Dustin Farris" notifications@github.com wrote:

ember-cashay is now on GitHub: https://github.com/ dustinfarris/ember-cashay

This initial implementation proves that cashay and ember can work together (and remarkably well, I might add).

Working now:

  • automatic generation of client-safe schema
  • cashay queries
  • integration with ember-cli-mirage for testing

Remaining before v0.1.0 release:

  • ember default generator to smooth out the installation process
  • better documentation (maybe a Twiddle to demonstrate usage)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/mattkrick/cashay/issues/120#issuecomment-257404621, or mute the thread https://github.com/notifications/unsubscribe-auth/AFQjv67NDw-wHZl50R8banlrYwUV1Fjcks5q5knEgaJpZM4J65tY .

dustinfarris commented 7 years ago

Update:

ember-cashay v0.1.0 has been released and works beautifully.

I am working on support for mutations (mostly just adding tests to make sure everything works as designed) and will then release v0.2.0 along with a blog post and a Twiddle (example app running in a sandbox that visitors can tinker with).

Getting the Twiddle working is currently blocked. ember-cashay browserifies Cashay, and Cashay+deps is huge which causes Twiddle to timeout when it is building the project. I am hoping that if #140 or something similar is merged, the size of Cashay will be reduced dramatically (no babel-runtime).

mattkrick commented 7 years ago

just merged your PR, thank you! Another large dep that i'd like to get rid of is GraphQL itself. Currently, aside from a handful of string constants, cashay just uses parse and print. If you have a good packager builder, then you can employ tree shaking to get just what you need. I don't think browserify does that though, so you're stuck with the entire thing.

dustinfarris commented 7 years ago

Update:

It gives me great pleasure to announce that I have a working Twiddle showing off ember-cashay!

ember-twiddle.com/f2a8a4123c65c4871a885444978efe65

The demo is very basic. Two queries demonstrated: one that returns a string, and another that returns results from a database lookup (via ember-cli-mirage).

There is a caveat with the Twiddle. The server graphql schema shown cannot be modified. This is because of the way Twiddle works—the asset-pipeline is only run when the addon is first installed, so the client-safe schema is generated once and once only. The rest of the application code can be tinkered with since it lives in the browser, but the graphql schema file is "for show" only.

Additionally, ember-cashay v0.2.0 has been released. This version mainly contains optimizations needed for Twiddle.

For the super curious, I had to create a wrapper addon to get this to work. More details in the ember-cashay-twiddle-demo README.

Next up: mutations

dustinfarris commented 7 years ago

Slightly better example (with sub-query!)

ember-twiddle.com/f2a8a4123c65c4871a885444978efe65

dustinfarris commented 7 years ago

Update:

ember-cashay v0.5.0 has been released

I can now say with a straight face that Ember+Cashay is a thing.

Mutations are tested and working well.

Browserify has been stripped (thanks to a broccoli-webpack plugin I found) so you can use cashay in Ember the same way you would anywhere else.

import Ember from 'ember'
import connect from 'ember-redux/components/connect';
import hbs from 'htmlbars-inline-precompile';

import { cashay } from 'cashay';

const usersQuery = `
{
  users {
    id
    name
  }
}`;

const stateToComputed = () => {
  const { 
    status,
    data: { users } 
  } = cashay.query(usersQuery, {
    op: 'UsersComponent',
    mutationHandlers: {
      createUser(optimisticVariables, queryResponse, currentResponse) {
        if (queryResponse) {
          currentResponse.users.push(queryResponse);
          return currentResponse;
        }
      }
    }
  });
  return {
    users,
    isLoading: status === 'loading'
  }
};

const UsersComponent = Ember.Component.extend({
  actions: {
    addUser(variables) {
      cashay.mutate('createUser', { variables });
    }
  },

  layout: hbs`
    <h2>All Users</h2>
    {{#each users as |user|}}
      {{user-details user=user}}
    {{/each}}
    <hr>
    {{add-user-form submit=(action "addUser")}}
  `
});

export default connect(stateToComputed)(UsersComponent);

This works well, but there is more to do to smooth out the edges. react-apollo has a neat helper (called "graphql") that connects a component to a query and mutation and does a bunch of other apollo-specific stuff. I'd like to do something similar but much simpler in ember-cashay that basically takes care of the redux subscription and props assignment for you without having to define stateToComputed.

e.g. something like:

import { cash } from 'ember-cashay';

const usersQuery = `
{
  users {
    id
    name
  }
}`;

const UsersComponent = Component.extend({
  layout: hbs`
    {{#each users as |user|}}
      Hi, {{user.name}}!
    {{/each}}
  `
});

export default cash(userQuery)(UsersComponent);

(obviously i haven't given the name much thought) — stay tuned!

dustinfarris commented 7 years ago

Update:

ember-cashay v0.6.0 has been released

This includes tests and support for @live fields.

The general usage works exactly as you would expect.

Testing was a little more complicated when introducing websockets. To make this easier, I brought in mock-socket to simulate a vanilla websocket server.

The async nature of websockets is also tricky. To smooth this out I've provided 2 new test helpers:

If you set everything up as I have, you can have a query like:

const projectQuery = `
{
  project (id: $project_id) @live {
    id
    title
    projectTodos @live {
      id
      description
    }
  }
}
`;

and write tests like this:

moduleForAcceptance('Acceptance | Todos');

test('live updates', function(assert) {
  const project = server.create('project', { name: 'IndustryMaps' });
  server.createList('todo', 3, { project });

  visit(`/project/${project.id}`);

  andThen(() => {
    assert.equal(find('.project-todos .todo').length, 3, 'initial todos should be rendered');

    // A new todo has arrived on the server!  Notify clients!
    window.socketServer.send({
      topic: `projectTodos:${project.id}`,
      data: {
        type: 'add',
        objects: [{
          id: "1234567",
          description: "Freshly Minted Todo!"
        }]
      }
    });

    andThen(() => {
      assert.equal(find('.project-todos .todo').length, 4, 'a new todo should be rendered');
    });
  });
});