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

BIG FAT ghost.js & index.js refactor #360

Closed
ErisDS opened this issue Aug 7, 2013 · 11 comments
Closed

BIG FAT ghost.js & index.js refactor #360

ErisDS opened this issue Aug 7, 2013 · 11 comments
Milestone

Comments

@ErisDS
Copy link
Member

ErisDS commented Aug 7, 2013

Please note: This is in dire need of doing, but it is also incredibly sensitive. Whoever takes this on needs to take care not to introduce a bunch of regressions, this probably means writing quite a few additional unit tests before tackling it.

There is some discussion of this in #52

My original intention was that ghost.js would provide the API used by themes and plugins to interact with Ghost - registering helpers and filters and such like. However, it quickly spiraled into a dumping ground (my fault) and now it's the place where several things live. It's also a bloody ugly singleton.

The main things that ghost.js now seems to be made up of are:

These things should be much more clearly separated, perhaps in separate files/modules.

The app initialisation needs to be more organised. Perhaps some of it should be middleware? The rest of it should be come much more managed, such that we have a single init function on which we wait in index.js if we need to. Additionally there is the ghost.loaded promise in index.js that I don't think we need at all.

The API for themes/plugins should be disentangled such that it can be offered up to themes/plugins without them getting all the other crud / access to things they really shouldn't have.

The internal API / state should be cleanly managed. Immutable properties should be frozen or managed with closures and providing only a getter.

The singleton pattern should be ditched.

Discussion, thoughts and ideas are very much welcome on this one!

@cgiffard
Copy link
Contributor

cgiffard commented Aug 8, 2013

In my opinion, anything with a session-specificity, or an object lifecycle within the space of one request should be middleware. But if the goal isn't serving a request, or performing a function tied to a given user session, the function in question should not be middleware.

We need to take advantage of the fact that we're a single long-running process, and remove as much as possible from the request chain. This'll let us do caching better, optimistic data-fetching, cross-request resource pooling, etc. And it'll lighten the load when a request comes in, allowing us to serve it faster.

It strikes me that there's a some stuff which only really needs to be done once, but is happening every request, like:

app.set('view engine', 'hbs');
// return the correct mime type for woff files
express['static'].mime.define({'application/font-woff': ['woff']});

While there are definitely things we need to do on a request basis because (for instance) we've got specific logic for admin & non-admin components, I feel that the more we can move to one-time configuration functions, or code decoupled from requests, the better.

ErisDS added a commit to ErisDS/Ghost that referenced this issue Aug 12, 2013
fixes TryGhost#392

- adds appRoot, and uses this to calculate other paths
- removes path calculations from loader
- remove the themedir setting in config.. completely unnecessary
- highlights just how important TryGhost#360 is
@ErisDS
Copy link
Member Author

ErisDS commented Aug 13, 2013

@cgiffard
Copy link
Contributor

It looks awesome @tgriesser!

As an aside, one thing I'm keen to look into is whether we can somehow turn initTheme into a one time configuration stage rather than middleware.

@ErisDS
Copy link
Member Author

ErisDS commented Aug 14, 2013

Indeed, it probably can be done, provided there is a way to change the theme without restarting the server :)

It seems to me, that moving initTheme out of ghost.js and temporarily into index.js, and then doing a small refactor for one-time config vs per-request config, with the ability to reload the one-time config, is probably a reasonably small and sensible first step on the journey to refactoring all of this.

@ErisDS
Copy link
Member Author

ErisDS commented Aug 29, 2013

I've created a branch called 'big-fat-refactor'. Anyone and everyone is welcome to submit PRs to it with their suggestions for refactors.

I've also started a wiki page with an audit of how the Ghost object is used: https://github.com/TryGhost/Ghost/wiki/Ghost.js-audit

The final outcome of this refactor should result in

  • No more singletons
  • Sensible one-time and request based configurations
  • Less stuff in ghost.js - it should be clear what it is for and shouldn't be a dumping ground. Stuff that doesn't belong should go elsewhere
  • A simplified index.js
  • A safe API to hand to plugins/themes for registering filters/helpers
  • The ability to require('ghost') from other projects and get a working blog
  • Much cleaner dependency graph
  • More readable/ easier to understand code
  • Some clearer idea of 'globals' - the result of config, settings, and state which is loaded (like availableThemes) and how they can and should be made available across the whole of the admin, and in a limited form to the themes.
  • And probably much, much more...

@ErisDS
Copy link
Member Author

ErisDS commented Aug 30, 2013

Audit is finished, I think it highlights lots of super-easy ways to improve the code.

https://github.com/TryGhost/Ghost/wiki/Ghost.js-audit

@sebgie
Copy link
Contributor

sebgie commented Sep 15, 2013

After working with the settigns api I think that all data should be retrieved from the api. There are currently different concepts of getting data:

  • ghost.dataprovider
  • ghost.settings()
  • instance.settings()
  • models.Settings
  • require('config')
  • ghost.config()
  • are there more?

In my opinion the api could be added to the ghost instance and then all access to data is done with ghost.api.

  • ghost.api.config.read('url');
  • ghost.api.settings.read('defaultLang');
  • ghost.api.settings.edit('defaultLang','de_AT');
  • ...

This would hopefully remove circluar reference of ghost.js and api.js (see mail.js) and reduce the size of ghost.js. settingsCache, dataProvider and config could be maintained within api.js and are only available with api functions.

All functions that should be available externally are mapped to an HTTP end point. All other api functions should remain for internal use only.

@ErisDS
Copy link
Member Author

ErisDS commented Sep 15, 2013

Firstly, it's absolutely correct that all of those ways of accessing data are awful, inconsistent and introducing weird and circular dependencies.

Exposing it all through an API sounds like an excellent approach to solving this.

The only complexity would be with initialisation. The api would require the database & models all to be setup, and they require config and settings etc. So migrations/index might have to have some special access to ensure everything gets setup in the right order.

Shall I raise a new issue to do this and assign to you?

@ErisDS
Copy link
Member Author

ErisDS commented Nov 4, 2013

Following on from where we are now in terms of this stuff, I figured I'd list out the deliverables, and turn this into an epic

A lot of stuff has been done to improve this already, including getting rid of the old initTheme function that was in ghost.js and lots of refactoring towards providing one way to get data via the API

@ErisDS
Copy link
Member Author

ErisDS commented Nov 26, 2013

@hswolff have created and assigned the death of singletons to you. Did you also want an issue around moving the register*** functions somewhere sensible? Perhaps you want to open that and I'll assign it? Want to keep the PRs small to reduce rebasing.

@ErisDS
Copy link
Member Author

ErisDS commented Dec 9, 2013

I think this is pretty much done now 👍 Moving to 0.5 so I remember to give it a bit of a review.

@ErisDS ErisDS closed this as completed Jan 20, 2014
daniellockyer pushed a commit that referenced this issue Jul 20, 2022
no-issue

We were incorrectly mixing transactional and non-transactional
operations. An e2e test in Ghost will be merged shortly which caught
this problem.
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

4 participants