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

[1.0] Query runner refactor #828

Merged
merged 10 commits into from
Apr 21, 2017
Merged

[1.0] Query runner refactor #828

merged 10 commits into from
Apr 21, 2017

Conversation

KyleAMathews
Copy link
Contributor

@KyleAMathews KyleAMathews commented Apr 19, 2017

Running queries to date has been a fairly brute force operation. Let's make it faster/slimmer/testable/understandable.

  • cache queries so only re-run queries when a component changes if the query itself has changed
  • persist results of queries so only re-run the queries on page startup that have been invalidated
  • tests for the add-page-data-dependency action creator
  • only write routes files if there's been a path change
  • refactor query runner into multiple testable modules: query parser, routes writer, path invalidator, query runner.

@gatsbybot
Copy link
Collaborator

Deploy preview ready!

Built with commit d9d60d2

https://deploy-preview-828--gatsbyjs.netlify.com

@gatsbybot
Copy link
Collaborator

gatsbybot commented Apr 19, 2017

Deploy preview ready!

Built with commit 6560744

https://deploy-preview-828--gatsbyjs.netlify.com

@KyleAMathews KyleAMathews force-pushed the query-runner-refactor branch from 8756144 to 89d880e Compare April 21, 2017 00:29
@gatsbybot
Copy link
Collaborator

gatsbybot commented Apr 21, 2017

Deploy preview ready!

Built with commit 836841b

https://deploy-preview-828--gatsbygram.netlify.com

@KyleAMathews KyleAMathews requested a review from fabien0102 April 21, 2017 00:51
@KyleAMathews
Copy link
Contributor Author

So I think this refactor is broadly correct. My main concern is with how sloppy some of the event chaining logic is. Not going to worry about it much now as this stuff will get worked over again in the future.

Copy link
Contributor

@fabien0102 fabien0102 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Big diff! Good works ;) just some styles comments/questions and maybe a bad copy/past find

Note: I haven't run the project on this branch for instant

@@ -305,8 +308,8 @@ module.exports = async (program: any) => {

console.log(`created js pages`)

return new Promise(resolve => {
queryRunner(thing => {
const finishPromise = new Promise(resolve => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This const it's really useless and finishPromise is a very strange wording ^^ (what this Promise represent? A variable name never should to be generic)

http://codebuild.blogspot.de/2012/02/15-best-practices-of-variable-method.html 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oops that was leftover from when I was experimenting with something. Fixed to just returning the promise.

* - Watch for when a page's query is invalidated and re-run it.
***/

const _ = require("lodash")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a general style reflection (es6 module vs require):

Sometime we use import ... from ... sometime const ... = require(), I think es6 module style is better for performance/readibility but more important I think we need to make a choice! (I always ask to myself "what is better for this file?")

So, what we choose?

ref: https://medium.com/the-node-js-collection/an-update-on-es6-modules-in-node-js-42c958b890c

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same fact from lodash, we have four import patterns:

  1. const _ = require("lodash")
  2. const {get, forEach} = require("lodash")
  3. import _ from "lodash"
  4. import {get, forEach} from "lodash"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The general rule I've arrived at (not universally followed) is to use commonjs for node stuff and imports for browser. I write enough "raw" node.js code that getting tripped up by es6 stuff not working when I'm not compiling the code gets really annoying.

For lodash, I just always import the underscore as I dislike going back and forth to change which exact methods I'm importing. When writing browser code, I then just add the webpack/babel lodash plugins to optimize the builds. Which speaking of, it'd be good to get a Gatsby plugin for this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Maybe it can be a good idea to add this "coding guidelines" into CONTRIBUTING.md (like https://github.com/lodash/lodash/blob/master/.github/CONTRIBUTING.md#coding-guidelines)

})

// Tell everyone who cares that we're done.
callbacks.forEach(cb => cb())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't try, but I think it's a good idea to add  callbacks = [] after (to avoid multiple callbacks calls)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callbacks (the array) isn't ever used again after this... which isn't obvious I agree. But this again points to the weakness of how I've been writing the (I'm not sure what's the right way to describe this) event based boot system.

I was thinking yesterday that SystemD might actually be a good model to follow since Gatsby is kind of an OS. Will read up more on that http://0pointer.de/blog/projects/systemd.html

If there's any other programming architectures you can think of for booting up an event-based system, lemme know.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can have a look into ionic-dev-tools , they've got many of our problematics (live-reload, watch, build, notifications…) and their code is very clean ;)

})

// Trim whitespace
if (query) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use lodash method (most robut => works with null)

query = _.trim(query)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


const debounceNewPages = _.debounce(() => {
store.dispatch({
type: `BOOTSTRAP_STAGE`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be good to have a constant.js or anything else to have a list with all possible actions (to avoid any collision).

Another good practice is to have an actionCreator even if the action is very simple (with this pattern, all actions are centralized, so it's easier to maintain)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is my first Redux project so I've heard of doing that but have been skipping it so far to keep moving quicker :-) But yeah, let's refactor things soon to use a constants.js

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like the pattern constants.js (you have 3 files to update for 1 action… so yeah, you moving slower) my best compromise to have flexibility and maintabiliy is to merge actionsCreator & constants (an example into my redux sandbox)

// If there's not an index page, just pick the one with the shortest path.
// Probably a bad heuristic.
if (!indexPage) {
indexPage = _.first(_.sortBy(pages, page => page.path.length))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a style suggestion:

_(pages).sortBy(page => page.path.length).first()

Quite same, but you retrieve the natural reading way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh nice! Haven't used this style before but I like it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chaining with lodash, I just love this notation 😄 Just some subtilities with lodashWrapper

I recommend this talk if you like it:
http://www.nicoespeon.com/en/2015/06/functions-chaining-composition-lodash-underscore/

@@ -41,6 +38,25 @@ if (process.env.NODE_ENV === `test` || process.env.NODE_ENV === `production`) {
)
}

// Persist state.
const saveState = _.debounce(state => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! It's better here ;)

module.exports = (state = {}, action) => {
switch (action.type) {
case "ADD_PAGE_COMPONENT":
if (!state[action.payload.componentPath]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of pattern affraid me (a bad past experience this object[a.b.c]…) It's proofer to use the lodash get (it's will not break if you have an undefined somewhere 😃 )

So for this => get(state, 'action.payload.componentPath')

(I know, normally we never should have undefined in payload, it's just a protection, it's not a big cost and really improve an application stability)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

return state
case "REMOVE_PAGES_DATA_DEPENDENCIES":
Object.keys(state.nodes).forEach(nodeId => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooo nice! Much cleaner.

)
})
Object.keys(state.connections).forEach(connection => {
state.nodes[connection] = _.difference(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is state.connections[connection] here

@KyleAMathews KyleAMathews merged commit f850a6c into 1.0 Apr 21, 2017
@KyleAMathews KyleAMathews deleted the query-runner-refactor branch April 21, 2017 17:01
@KyleAMathews KyleAMathews changed the title [1.0 WIP Query runner refactor [1.0] Query runner refactor Apr 21, 2017
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

Successfully merging this pull request may close these issues.

3 participants