TryGhost / Ghost

Independent technology for modern publishing, memberships, subscriptions and newsletters.
https://ghost.org
MIT License
46.75k stars 10.19k forks source link

[Ember.js] admin UI rewrite #2271

Closed ErisDS closed 10 years ago

ErisDS commented 10 years ago

Code: https://github.com/TryGhost/Ghost/tree/master/core/client

The following is an outline of the admin UI as it stands, and all the pieces that need to be rebuilt in Ember.js

Build tools:

All routes (except signin, signout, forgotten, reset and signup) are protected through login => when logged out redirect to signin (applies to all remaining routes)

Notes on future behaviour:

For development we will write a stupid express server that mocks the current API endpoint and static responses for all requests, @sebgie will lead this

Additionally needed API endpoints:

Documentation available on the wiki: https://github.com/TryGhost/Ghost/wiki/%5BWIP%5D-API-Documentation

Ember specific discussions

hswolff commented 10 years ago

dibs :P

halfdan commented 10 years ago

Ignore @hswolff :D Dibs!

manuelmitasch commented 10 years ago

Should we start a separate issue on discussing the model layer or are we already sold on "very thin to begin - later investigate more complex options"?

The suggestion by @rjackson was to use a custom Ghost.Model with find and save methods using ic-ajax.

Once the rewrite is finished we can still use more advanced libraries such as Ember Data, Ember Model, Orbit.js, and friends.

stefanpenner commented 10 years ago

Tooling:

just a heads-up, ember-cli work is progressing nicely. It aims to replace EAK especially the plethora of brittle and slow grunt tasks. It aims to be compatible, but will dramatical improve developer ergonomics.

As the project improves, I will try to keep your ember project in sync.

Persistence:

I would also encourage using ember-data directly, rather the shimming it in later. It is easy to use ember-data as a model + ajax layer without touching some of the more complex features (relationships and such) this will resemble your use of Ghost.Model but will instead benefit from a community of maintainers and best practices.

If you all have further questions feel free to reach out to me, I can likely spare small bursts of time.

jackfrancis commented 10 years ago

For Your Consideration (in terms of approach and perhaps, what not do do), something I whipped up as a response to Ember Data roadblocks (mostly due to backend factors: our API was not as Rails-y as the Ember team prefers, a slightly different approach to route paths, etc.; paginated result sets were especially a challenge for us to solve with ember-data at that time).

https://github.com/askcom/quarry-webui/blob/master/app/quarry-data.js

And example of a model object that extends the 'Quarry' data persistence layer namespace (for lack of a better term):

https://github.com/askcom/quarry-webui/blob/master/app/models/asset_model.js

I borrowed ideas from Discourse (at the time their codebase was not ember-data-enabled, not sure about now), especially the ajax function here:

https://github.com/askcom/quarry-webui/blob/master/app/quarry-data.js#L363

(that code block suggests @ErisDS's remark about "thin wrapper around ajax calls".

This is not the most elegant solution, and the .reopenClass() pattern is weird to the uninitiated, but this type of approach can work.

I agree with @stefanpenner that adoption of ember-data will yield the most long-term benefit, caveats about keeping very up-to-date with the ember-data community as it approaches 1.0 (and breaking changes are inevitably introduced). We chose to roll our own (this was 18 months ago so more defensible then) and wanted to share that this is a possible option.

stefanpenner commented 10 years ago

i'll gladly work with someone on the minimal ember-data usage, that basically exposes the raw ajax requests on a per model basis (via per model adapter) but still benefits from much of the internals and most importantly if problems arise a community is present.

manuelmitasch commented 10 years ago

@stefanpenner What do you think ghost would benefit from Ember Data other than an identity map? AFAIK the data structures for the ghost admin currently is always one API endpoint per route. Of course, this does not mean the models could not be structured differently. I have stopped using ED approx. 1 year ago as I could not get rid of some nasty bugs. This was before the jj-abrams reboot, so things might be a lot better now.

Are there any good examples where ember-data is used to additionally enable offline-capability? I think we would at least need to write our own adapter that integrates offline-storage as well.

In my view it mainly adds unnecessary complexity and payload. I believe if we use ED from the beginning we would have more work in case we would like to use orbit.js or something similar. What do you think about that?

stefanpenner commented 10 years ago

@manuelmitasch a lot has changed in a year, even ember a year ago was dramatically less mature.

  1. consistent patterns and api (like the rest of ember)
  2. a ever improving project (independent of ghost's efforts)
  3. ever growing community + knowledge base

Given the following:

// models/post.js
export default DS.Model.extend({
  firstName: DS.attr('string')
});

Assuming your API is a bit non-standard, per model adapters are you best-friend.

// adapter/post.js
export default DS.RestAdapter.extend({
  find: function(store, type, id) {
    return /* ajax call for find, which returns
                 a promise that fulfills with the data */
  },
  // implement functionality as needed (if it is non-standard)
});

see: https://github.com/emberjs/data/blob/master/packages/ember-data/lib/system/adapter.js#L111 for the rest of the Adapter API

In addition to the above, using ember data will provide you with.

Now if you decide to add offline support, you have a clear and concise place to put that functionality (the adapter). If you (or someone else) releases such an adapter it would be of interest to others in the community, which intern could mean more contributions and a better adapter.

Given the possibility of a later migration (to something like orbit), I would prefer my usage of persistence to be consistent across the board.

That being said, given the architectural choices of ember-data and orbit.js, I wouldn't rule out orbit.js working as a super-charged adapter layer for ember-data.

jackfrancis commented 10 years ago

@stefanpenner Those are great, simple examples of the kind of customization that was not (obviously) accessible to the ember-data newbie when we rolled our custom adapter code in late 2012. Additionally, we built the core of our main app in late 2011, and as such had to endure a plethora of breaking changes and refactors during the maturation and lead-up to 1.0. This experience frightened us away from doing that all over again with ember-data in a pre-1.0 state (of course this remains the case).

However, based on two years of working with Ember, my faith in the core team’s architectural decision making is pretty high (this despite tons of little frustruations during this journey!), and so I’m much more likely now to +1 investing in ember-data from here on out.

rwjblue commented 10 years ago

I agree with @stefanpenner. We can almost certainly customize Ember Data to our needs (and I know there are a few contributors to that project in this thread to unblock us on any issues).

Ember Data gets us off the ground with a good base with many features that we would likely not have implemented ourselves.

stefanpenner commented 10 years ago

@jackfrancis although my time is limited, I would like to provide any help I can, especially at the start of this project. feel free to ping me on irc #iamstef

ErisDS commented 10 years ago

So it seems there are strong feelings that we should use Ember Data? It was my impression that this was a pretty heavy component that we might not even need.

@stefanpenner I see the benefits you've laid out, are there any drawbacks we should be aware of?

I was very keen to start light touch and build heavier duty stuff in when it became apparent we needed it, but I'm not familiar enough with all the options just yet so am very much open to suggestion.


In terms of an approach to getting the rewrite done, it has been proposed that we use a 'dumb' API that returns static responses.

I think we should also turn authentication off completely to start with, and focus on building it back in once we have some of the core content/editor/settings screens in place?

This should be relatively easy if we're going to build this at /ghost/ember/ for the time being as we can just add it to the list of noAuthRequired routes.

jackfrancis commented 10 years ago

For static API responses (I take that to mean mocks), I've had good success with the jquery-mockjax library.

https://github.com/appendto/jquery-mockjax

stefanpenner commented 10 years ago

@ErisDS extra abstraction === learning cost, some advanced features (some aspects of relationships) may not be fully fleshed out.

Ultimately it is up to you guys but I feel strongly the simple use-cases is easily accomplished with ember-data, with an upside of good separation of concerns. As more complexity arises it provides a foundation to build on.

Globegitter commented 10 years ago

@ErisDS If the API doesn't return the response that ED expects there is definitely some extra work involved and it does add some weight to the total code size, but as Stefan pointed out customization has gotten much better over time. I have been using ED since rev. 0.12 myself and a few months after it got in the beta phase I started to see it as more mature and feel comfortable using it in production (which I am doing). Some nice things that I have gotten to like while using it: Caching, how it just handles the loading the same resource multiple times, automatic loading of the right model based on the route and compared to using it the first time it now much more has the feeling of 'it just works'. Edit: Oh an not forget the ability to use ember-extension can sometimes come in really handy.

On a completely different note: Depending on what work there needs to be done I would love to help out on the project itself. Let me know if and how I could get involved some more.

manuelmitasch commented 10 years ago

I'm okay with using Ember Data. It's obviously an amazing piece of software. So it seems the suggested way is a per model adapter that could use a custom ic-ajax call. @ErisDS what are your feelings about it after this discussion?

manuelmitasch commented 10 years ago

One thing that we did not really discuss is the folder structure. In EAK we have an app folder where all the app relevant files go. On the same level there is also a config and test folder. I believe for acceptance tests the existing Casper.js tests could be used. Although, I like the idea to compare an Ember app to a iOS/Android app and would prefer to have qunit tests that are placed inside the client folder.

What are the feelings about the folder structure inside the app folder? They can be organised by type (all controllers into controllers folder, ...) or in PODs (eg. route, controller, template for editor in one folder). By type is well established, in PODs seems nicer for big apps, but has the drawback that it is not always clear where to put files that do not strictly belong into a specific POD. I think as the admin interface is not very big we can stick with the by type structure.

hswolff commented 10 years ago

By type works for me. When I implemented the EAK folder structure I didn't commit all the empty files with .gitkeep as those aren't necessary to be in the repo. I figured once we had controllers, routes, etc we'd add them into their appropriate type folders.

Also we have a test specific folder at the moment, it'd be nice to centralize all the ember tests there.

rwjblue commented 10 years ago

I personally like the pods structure, but the EAK resolver falls back to lookup by type after attempting to lookup via POD. So it is easy to mix and match as makes sense. For example, things that are global to the application (like models and reusable components) can just be stored in their respective folders by type.

Either structure works well though.

ebryn commented 10 years ago

I expressed my opinion on what the Ghost team should do re: data/model layer during our hangout call today. Here's a direct link to that part of the call: http://www.youtube.com/watch?v=GsN1diIdT0o&feature=share&t=1h15m19s

TL;DR: KISS (keep it simple stupid)

  1. Start with $.ajax
  2. Build up a tiny model class on top of that to get some things for free (like the default model hook behavior with dynamic segments)
  3. When you feel the need, look into data libraries like Ember Model and Ember Data.
sebgie commented 10 years ago

I have opened some issues for API responses ( #2347, #2348, #2349, #2350) yesterday and I think that the ember implementation could already use the new API responses.

After doing some research and a short discussion with @ErisDS we think that @jackfrancis suggestion of using a mocking library to simulate the API responses is a better solution than my initial idea of building an API server. jquery-mockjax seems like a good solution if everyone is happy with it? I can do the setup for the mocks and fill in the initial API responses, if someone points me to where they should go :-).

jackfrancis commented 10 years ago

Happy to help with any mocking tasks. I recommend creating a thin wrapper around mockjax to make the actual mock invocations a bit easier to digest. For example, with something like this:

var MockApi = Ember.Namespace.create({
    ajaxStub: function (path, http_verb, http_data, http_response) {
        return $.mockjax({
            url: 'http://myapi.example.com' + path,
            type: http_verb,
            data: http_data,
            dataType: 'json',
            responseText: http_response
        });
    }
});

You can do lots of these:

MockApi.ajaxStub('/ghost/api/v0.1/posts/1*', 'GET', null, MockApi.post_response);

As opposed to the more tedious:

$.mockjax({
    url: 'http://myapi.example.com/ghost/api/v0.1/posts/1*',
    type: 'GET',
    data: null,
    dataType: 'json',
    responseText: MockApi.post_response
});

(Assuming that MockApi.post_response is a literal object that reflects an API json response payload.)

I had trouble getting url strings to match without the wildcard suffix, but always hoped that was my own misunderstanding. (To explain the asterisk in both examples above.)

manuelmitasch commented 10 years ago

@sebgie I agree that a 'client-side' mock should be enough for the development at the moment. We are already using ic-ajax which has the possibility to define fixtures per URL. Thus, I see no need to integrate mockjax as we already have the functionality.

See file: https://github.com/ErisDS/Ghost/blob/ember-with-proto/core/client/fixtures/init.js

manuelmitasch commented 10 years ago

As a followup on @sebgie 's API response issues I have opened an issue to discuss a possible standard/spec/media-type for the JSON REST API.

ErisDS commented 10 years ago

A quick look at the list in this issue shows just how much of the Ember admin is complete. Still a lot of things are in a not-quite-finished state, where they mostly work but some of the finer points are missing.

There are 5 main areas of concern:

1. Validations

Covered by #2856 and #2976

We need a foundation on which to build validations for all aspects of the app. Updating posts and signin/signup are incomplete due to a lack of validations. Settings/General will also need this to be completed. @darvelo is working on this, but opinions and ideas are very welcome.

Required by: #3036, #2893, #2850 as well as #2856 and #2976

2. Mobile Support

Foundation issue: #2333 + #2957

We need a foundation on which to build a solid mobile offering. This is going to be time consuming, so in the short term we need to at least re-implement the 'mobile interactions' that we had in the old admin - #403 serves as a list of differences / special things we served to mobile devices.

Additionally, we need to not regress on the editor, and ensure that we can still serve codemirrorMobile to touch screen devices.

3. Keyboard Shortcuts

Foundation issue: #2752

Yet another missing foundation, we need the tools to add various kinds of Keyboard shortcuts. #2752 will be considered closed once the handful of shortcuts outlined in https://github.com/TryGhost/Ghost/issues/2752#issuecomment-45213684 are implemented.

Required by #2988 and #2984, there will be many more of these #1463 acts as an epic for the whole process.

4. Wiring in Ember Data

Issues: #2951, #2846

So far, only the posts actually use ember data. We need to wire up the rest of the admin to also use it. There are two issues so far and more to come.

5. Tests

Covered by: #2989, #2990

At the moment we're flying blind with the ember admin, never knowing if a change we are making is breaking some other feature elsewhere. Step one is to get the old casper.js test suite running against the ember admin, leaving any tests that won't run due to incompleteness commented out with an issue marker where possible. Step two is to get a new suite of unit tests up and running against the most sensitive parts of the app (managing posts).

ErisDS commented 10 years ago

Additionally, here is a shortlist of the last big pieces of functionality left to complete in Ember. Essentially this is the same list as the one at the top, but with all the stuff we've completed removed so it's easier to see: