Closed brenmcnamara closed 7 years ago
Things to fix before merging:
There need to be clear instructions on how to generate a distributable badi.js file for browsers. There should always be a folder with a final javascript file in the repository so someone can, if they like, download a version of the library that's compatible with all major browsers. They shouldn't have to download or install any tools, and they shouldn't have to look hard. It would be preferable (but it's not necessary) if there were a human-readable file for this purpose (the packaged code is a real pain to read).
Right now, it's possible to merge the master branch into the gp-pages branch so that github can host the example. It's okay if that particular mechanism no longer works, but we need an automatic way to publish to github pages. One option would be to host all the website pages out of the /docs folder and provide an easy way to repopulate that folder with a newly-built badi.js.
Function/variable names need to represent what functions/variables
do. For the most part it's okay, but some names changed in ways
that obfuscate behavior. For example, find_sunset
is now called
getGregorianDateForSunset
. What this function actually does is it
calculates the time of sunset on a given gregorian day and returns
that in a date object. However, the word "For" in the function
name implies that you give the function a "Sunset" and you get a
GregorianDate back, which doesn't make sense. A better name would be
getUTCTimeForSunsetOnDate
or maybe getSunsetTimeOnGregorianDate
.
Frequently, the name "Gregorian" is misused, and it would be more
meaningful to write "UTC". UTC corresponds to universal time without
regard for a system of calendar (except that internally its stored
in a gregorian way). Some of these "Gregorian" variables really
represent a moment that's significant on the Badi calendar and not
the Gregorian one. Also, keeping track of which dates are in UTC and
which are local to a place is often the real chore, whereas keeping
track of whether something is stored in a "Date" object isn't. For
example the gregorianSunset
and gregorian8NewMoons
variables in
getGregorianDateForTwinBirthdays
. There's really nothing "Gregorian"
about these value other than the fact that it's stored in a date
object. However, it represents a specific UTC time. It would be more
sensible to call them UTCSunset
or UTCNewMoons
. With all the name
changes, understanding what the function does now is harder.
Helpful comments that were previously in the code are missing. For
example, find_naw_ruz
used to have the comment "Gets the day of Naw
Ruz in a given gregorian year. It returns the day at 00:00:00 UTC". I
haven't looked for other missing helpful comments, but if there are
any, they should be replaced (I just came across this because it was
relevant to one of the questions).
Conditionals.
The syntax (conditional ? a : b)
is awful to read. An if-statement
is almost always preferable. It's not worth using this syntax just
to make a variable const -- let it be mutable!
There is no advantage in using this conditional notation on a statement that would take up multiple lines anyway. An if-statement is always more clear than this.
Even in the most extreme cases where use could be justified, like
x = function(foo, bar, oof, doof, goof, ( awesome ? 3 : 4 ))`
I prefer (although this is just me),
if(awesome)
hoof = 3
else
hoof = 4
function(foo, bar, oof, doof, goof, hoof)
Remove comments with "QUESTION". I've answered them below.
There are several yarn.lock files in the branch which I assume shouldn't be there (this is for maintaining local machine state, right? I could be misunderstanding).
Things I would like to see fixed, but that I won't insist on:
Many functions are in their own file. This just creates more work for
to edit the project. Relevant functions should be grouped together.
At least getGregorianDateFor{NawRuz,NextNewMoon,TwinBirthdays}
could be together.
I don't like camelCase. If possible, I prefer underscored_names unless there's a really good reason to change it. At least just CapitalizeEveryWord.
I prefer that file names don't start with underscores unless there's a technical reason why it should be that way.
There are 544 folders in node_packages
in the source tree. Do
these need to be there? If so, why? Do users need these packages or
just developers?
It's sometimes not worth wrapping a line of the form let variable = function(foo)
into two lines.
If the line is just barely too long, the cleanest solution may
be to make the variable or function names shorter. This is the
case, for example, with the line that declares gregorian8NewMoons
in _getGregorianDateForTwinBirthdays
. It might also be okay to
wrap lines a little longer. I'm guessing right now it goes to 80,
but I'm okay with it as high as 100 (it'll still fit on 1/2 of my
screen). Middle-ground like 90 is fine too. But that's up to you.
In general I'm a little disapointed with the added complexity. This library was designed to be a one-file drop in that shouldn't be hard to use, hard to develop or hard to setup. Now, anyone who knows only javascript could work on the whole project. With these changes you also need to have some level of familiarity with node, webpack, jest, and babel to work on the project end-to-end (and probably a CI tool, coming soon). If there's anything we can do to reduce the number of tools I would appreciate it.
Along these lines, I would appreciate a reminder on why this packaging is helpful. I don't see improvements to ease of development or library use. Does the packaging really make it easier for developers to use the library on other platforms? If so, how do they do this? Where do they find instructions?
Questions:
fromGregorianDate
: We are getting the UTC year here, is that what we want?
Yep. We could already be a whole year off anyway depending on when Naw-Ruz is, and so the if-statement a few lines down corrects this. Getting the "localtime" year would be very involved because it would require tracking down the timezone of the given latitude/longitude coordinates.
fromGregorianDate
: Why are we subtracting two days?
getGregorianDateForTwinBirthdays
: Why are we getting sunset for naw
ruz date? Is it not already sunset.
From the comments in the original source code of find_naw_ruz
:
"Gets the day of Naw Ruz in a given gregorian year. It returns the
day at 00:00:00 UTC." Thus, you need to get the sunset time in
Tehran on this day to find the moment the year starts in Tehran.
There's a good reason for this. The year starts at a different time
for every place in the world. It starts at sunset, Tehran time, in
Tehran. But it will start earlier in India and China (when the sun
sets there) and later in Europe and the Americas. Forcing the time
to be at 00:00:00 makes it clear to the consumer of this value that
they need to really think about what it means to have a Gregorian
day which "corresponds" to Naw Ruz.
Regarding the questions about getGregorianDateForNextNewMoon
:
The BlueYonder function MoonQuarters returns the day/time of the new moon, first quarter, full moon, and third quarter in an array of length 4 (Julian dates). jdtocd is a function that converts Julian dates to Gregorian ones (I didn't name that function...) The problem is that it might return the wrong lunar cycle and give a new moon date prior to the date we're looking for. So a second call to the MoonQuarters function needs to be made -- this time with a date that's squarely in the next lunar cycle. This function could be written without recursion, but it would essentially require duplicating the code that's already there or writing a loop. I think the recursive one is nicer, although admittedly this code is tricky and could use a few comments.
getGregorianDateForTwinBirthdays
: Why are we setting to sunset in the
previous line if we are just getting the day in the next line?
I'm not totally sure. It's possible that this was intended to compensate between the time difference of Tehran and UTC, but I don't think it's actually necessary. Another possibility is that an older version of the code was returning the time, and then it got changed to the current interface without removing the dead code. If all the tests pass with this statement removed we can take it out.
Correction -- I see now that node_modules is in .gitignore. I think I ran "git status" and saw no results so I thought maybe that some were from the branch, but that's not the case. You can safely ignore that bit of the comment.
Also, I just tried this. What went wrong?
$ npm run test
> badi-cal@1.0.0 test /home/berkeley/badical/badi-cal
> jest
FAIL src/__tests__/_getGregorianDateForNawRuz-test.js
● _getGregorianDateForNawRuz › gets the correct naw ruz date
ReferenceError: EquinoxCalc is not defined
at Object.vernal_equinox (src/extern/stellafane.js:20:12)
at gregorianDateForNawRuz (src/_getGregorianDateForNawRuz.js:19:77)
at Object.<anonymous> (src/__tests__/_getGregorianDateForNawRuz-test.js:15:79)
at process._tickCallback (internal/process/next_tick.js:103:7)
_getGregorianDateForNawRuz
✕ gets the correct naw ruz date (6ms)
✓ handles the edge case of year 2026 (2ms)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 2.258s
Ran all test suites.
npm ERR! Linux 3.16.0-4-amd64
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "test"
npm ERR! node v7.5.0
npm ERR! npm v4.1.2
npm ERR! code ELIFECYCLE
npm ERR! badi-cal@1.0.0 test: `jest`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the badi-cal@1.0.0 test script 'jest'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the badi-cal package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! jest
npm ERR! You can get information on how to open an issue for this project with:
npm ERR! npm bugs badi-cal
npm ERR! Or if that isn't available, you can get their info via:
npm ERR! npm owner ls badi-cal
npm ERR! There is likely additional logging output above.
npm ERR! Please include the following file with any support request:
npm ERR! /home/berkeley/badical/badi-cal/npm-debug.log
I just had an idea about variable naming (which we can do after merging in your change set). As far as I can tell, we only store two kinds of values in a javascript Date object:
I think we should use different names for the first two kinds of variables (e.g. UTCFoobar or GregFoobar), and introduce new variables to account for the third kind. This would certainly be an improvement over the original variable names, which admittedly aren't that informative. I can do this after we merge your changes, so there's no need for you to worry about this.
As an example, this was partially done in find_naw_ruz
in the current version of the code, where some variables ended with _day and others ended with _utc. But that wasn't systematic. What would actually be really ideal is to rewrite the code with a different type for a gregorian day (e.g. month/day/year) and only use Date objects for UTC. I don't know if doing that refactor is worth it though.
As far as I can tell there are no other "types" of variables where we store time, but if for some reason we ever stored something with localtime we might name that variable differently.
This looks like good progress! I have a few more questions. Most of these, I think, will just be a matter of updating the documentation:
It looks like there's a typo in the README.md; I think we want npm run create-build
rather than npm run create-bundles
.
What is the src/examples
folder supposed to contain? I'm a little confused because src/examples/example1/dist
seems to have compiled (and stale) javascript from the previous commit.
What's the difference between browser-build-v1 and browser-build-v2? Perhaps you can explain this in the README?
It looks like there are some stale files in build-v2 from the previous commit; how do we clean those?
Is there some kind of cleaning step that users can run (e.g. from npm) that will clean all the built files so we know we're starting from scratch? (and document this in the README too)
The divisions of functions into files makes sense I think. I agree separating data out is a good idea. I'm noticing that the functions in the "util" file are generally doing astronomical calculations (returning the UTC time of a particular event), where as the BadiDate.js
file is performing computations specific to a calendar (e.g. counting months or days in relation to the astronomical events). So maybe we can instead call that file Astronomy.js
or something like that? I'm sure there's better names but that will do.
The current README explains, in broad terms, what all the different source files do (i.e. the extern folder, badi.js, and the demo code). Could you add a bulleted list into the new README that explains the same things? For example, what does each subdirectory of src/ contain, what kinds of functions are in each of the different files, etc.? The sections of the README can be ordered: Purpose, Environment Setup, Running the Examples, About the Code (new section), Before Committing, Status, and Specs. The "Before Committing" section is good and important, and I wouldn't want it to be lost at the bottom. You're welcome to add more of course... anything that helps explain the code, the tooling, or how to contribute is a plus.
I really want to be able to have the Github hosted example application for badi-cal stay up-to-date, even if it's not fully automated -- and I think there's a very easy way to do this. Publishing to github only involves putting all the files we want to host into a single folder. It doesn't even need to be the root folder. So, if create-build
could also build this example, we would be set. We would just need to create a new 'index.html' page that redirects users to the new URL. My guess is that doing this is very easy, and I'd like to see that work before merging. I worry about postponing it because it might never get done (at least, I probably wouldn't have time to do it).
It used to be that the "example2" (harness.html
) was the only testing infrastructure. That code was written to offer reliable testing with maximal simplicity and no dependencies. Now that we have a more formal test suite, it seems silly to keep it. It does a good job, however, on exercising the code on all the published Naw-Ruz and Twin-Birthday dates. I wouldn't want to accidentally ship code that doesn't get those right, and right now the new test suite is the only one mentioned in the README. I think we have two options. One -- the quick and easy way -- is to just update the README to say ensure that both example1 and example2 work before committing. The other -- which I would prefer -- is refactoring the example2 tests into the unit test framework. It should be pretty easy to port these (just changing the testing utility functions I use into the ones used by the unit test framework -- much less than the work that's already done!) and I think important for reliability.
Regarding Variable Names
I'm okay with whatever happens at the moment, and I won't hold off on merging because of variable names; it's simply not worth worrying about for this project. Realistically, I'm probably not going to have time to rename the variables once this is merged, so whatever is done here might be final. Every person, organization, and project has its own preferred conventions. I opened an incognito tab and googled for "javascript variable naming convention" (without quotes) and the first result was this. They use underscored_names
for variables, but camelCase
for method names. There are, of course, many javascript style guides that say to use camelCase
for all variable names, including Google's. There are also some that name both options, because it's a choice.
In a given project, it is usually best to go with what is established because making large changes to a codebase can often cause problems -- like merge conflicts, refactoring bugs, obfuscating version control history, etc. The exception is if there's a problem with the current variable naming scheme (e.g. it's completely unreadable, like some of our external dependencies are...) so the developers can build a unified vision about what they want to move to and do it together.
Fixed documentation to use npm run create-build
On the topic of stale javascript in src/examples
Stale JS has been cleaned up, examples have been moved out of the src/ folder into the root.
I will add some documentation in the readme. V2 is just a different API than V2. The methods / state exposed is different, and a lot of stuff that you can do in V1 is removed in V2. The reason for this is that I would like to (1) separate out any state about the holy days and UI objects and have a javascript library that can just do the calculations for the dates, and (2) remove any functionality that is not directly related to calculating to and from BadiDates (such as functions on calculating sunset of days, etc...).
We can discuss what to add back and what is necessary, but I would like to start V2 from a clean slate and build up instead of starting V2 with all features of V1, then tear down. The reason is if people are using V2 and we're tearing down functionality, then this would break the version, so we would need to increment the version every time we remove something. If we start from a bare bones implementation and add stuff, no one's code will break, and we can keep V2 for longer and not worry about submitting breaking changes.
In a nutshell, V2 only has the BadiDate object. V1 is everything that was there previously.
Sorry about that, I cleaned it up. The process of how to clean the build is addresses in 5.
I've added the command npm run clean-build
and documented it in the README
Renamed util.js to Astronomy.js
Created a About the Code section and reordered some of the sections
On the topic of hosting the example
You should be able to host the example by changing the URI to point to: /examples/example1/index.html. I've never hosted directly from Github, but from the stuff I've read, it looks like you just need to provide the path. So all the coding work is done to allow the example to work, it looks like you just need to specify the path to point to, which I believe must be done outside the scope of this commit.
I have gone with option 1 and noted in the readme that harness.html should pass on all tests before committing any code. The reason why I chose option 1 was that I would like to get this diff out sooner than later, and I want to take my time on converting the test suite, adding more tests on parts that I feel need more attention, and setting up hooks that check for eslint error and testing errors before allowing the person to commit. This will be the very next thing I add, but for now, I would like to postpone migrating the test suite.
If you feel strong enough about underscore, then we can move over to that syntax. However, I would highly recommend using camel case.
I'm not sure where you found the documentation you linked to in your comment, but by the use of the older es5 syntax, I can see it is outdated. Javascript is the language I use the most and I have never seen any widely-used library / framework that breaks with the camel case naming convention. When people are using this library, they will be using the convention adopted by the community and introducing a library that breaks with the convention will result in other people's code mixing standards.
Tabs vs Spaces, function vs shorthand, single quotes vs double quotes are all examples of styling conventions that come down to preferences, and if contributing to libraries that don't match your personal preferences, you should absolutely go with the established convention. However, variable naming is something that affects the code of the end user. In a community that has strongly articulated a preference for camel case, I would like to stick to the community preferences. I don't see this as a 50 / 50 or 80 / 20 thing where there are plenty of examples of underscores being used in other libraries. This is closer to a 99.5 / 0.5 preference towards camel case.
I've already ported everything to use the camel case naming convention, so I don't think merge conflicts are a concern at this point. If you say that we're using the underscore convention, then we will port things over. Please let me know what the decision is.
I'll go ahead and merge this now. The only lingering question I have: it looks like there's still a src/examples/ folder that only has example1 in it -- do we need that?
I'll make tickets for the following things to do:
I've made some tickets, you're welcome to work on them if you like. If so, just assign them to yourself so that I know not to work on them.
I added a command
npm run create-build
that will build the javascript and deposit it into "browser-build-v1", "browser-build-v2", and "build-v2" directories. The "browser-build" directories can be imported straight into a browser the way you would normally do it and "build-v2" is not bundled, assuming that you are working in an environment that supports some module system. I made of note of this in the README file.I understand that the packaged code is unreadable, and I feel that it should be left that way. If someone wants to read the code, they should be looking at the source code. People shouldn't be reading the packaged code, analogous to any other language where you wouldn't expect people to read compiled binaries. In the future, I would like to add things like an "uglification" step, to compress the javascript and make the library more efficient, making it even more unreadable.
That's definitely an awesome idea to have documentation and a built javascript file continuously pushed. I would like to save the work for after the pull request, if that's okay. I feel that this is a significant project on its own.
I renamed the variables / functions by your recommendations. When coding, I prefer that the data type of a variable or return value of a function be clear in the naming. I've ran into a lot of trouble in the past, especially with date objects, where people give un-descriptive names like "timestamp" or "date" without any clear indication of the format (timestamp in seconds / milliseconds? Date according to what standard and format?). This is why I tend to name my variables with names that express the class type in which the date is being storing "gregorian is javascript Date, badi is BadiDate". I understand your argument for the name "UTC" over "gregorian" for relevant cases, and that sounds reasonable to me. I made the changes. I apologize in advance if I missed any renaming of dates. I see you also added a recommended naming scheme in a later comment. It sounds like a great idea. I would like to delegate that work to you if that's okay, you have a better understanding of the semantic differences between gregorian and utc dates than I do.
I went through the original code in master and migrated any documentation that I found missing. Again, I apologize in advance if I missed a spot.
I went through and replaced all ternary uses with if / else. Personally, I prefer the ternary operator for a couple of reasons. First, it's more succinct. I've seen it used enough in code, that I read it as comfortably as any if / else statement. Second, I prefer having my variables assigned in one statement on avoid mutability. The "let" keyword indicates an intent to re-assign a variable. In a long block of code, reading a "let" puts in my mind that the variable will be reassigned later, and when the re-assignment is done only because of a conditional initialization, I still have to make sure that I understand the value of the variable throughout the function. Const variables give me assurance that the variable is consistent throughout the function.
With all that being said, I don't feel strongly enough about this to push back, as long as the style is consistent, I'm fine with either approach.
Remove QUESTION from files
On the topic of yarn.lock
The yarn documentation specifies that yarn.lock should be added to source control. Here is the relevant documentation. Check the section titled "Check Into Source Control"
I moved the 4 main functions you list into a file called _util. If you have a better name for that file, I'm up for suggestions (I don't like naming things "util"). I would like the holy days and the geo-location information in separate files. The reason is that they are data, not code, and I don't like to mix code and data.
The reason I moved things to camel case is because that is the known convention for javascript. All the public API's and any third-party libraries follow this convention. Upper camel case is used for classes, lower camel case for functions, methods and variables. Constants don't have a clear convention, I've seen the following:
HERE_IS_A_CONSTANT, HereIsAConstant, hereIsAConstant
. I tend to straddle between the first two conventions, but I'll let you decide on it. There are not many common conventions in javascript code, but camel case is one that is consistently seen. If we are introducing a public library, we should stick with whatever conventions the developer community expects.Changing underscore files to not contain underscore. In general, however, I use the underscore convention to denote private. Private variables / methods / files are not enforced in javascript, so this is the naming convention I like to indicate that I don't want people to touch my file / method. In Facebook, underscore methods are enforced by walking through a class and renaming all the methods that start with underscore, so that any caller outside of the class will break if trying to access the method. I don't feel strongly about having underscore filenames, but I would like to stick to the convention of underscore methods == private.
I am adjusting the eslint configuration to enforce 100 columns instead of 80. I typically stick to 80 so that I can have multiple side-to-side panes of code and read comfortably, but I don't feel strongly about this. Many of the variable names become short when I converted gregorian => UTC. In a later comment, you made the suggestion of using "greg", which I'm fine with also.
I understand your concern about the increased complexity of this project. babel, webpack, node, and yarn is a lot more tools than a simple javascript file that can be added to a project. This added complexity is the cost of having a javascript implementation that is portable and accessible. The hope is that with good documentation, pre-made builds, and possibly some CDN deploy that contains the built javascript for people to reference, the need for in-depth knowledge from the user is not needed. Oliver needs a version of this that works with his app that he developed, but the original version of this project is not usable in his javascript environment, so the 2 options available are to fork this project or to modify it to support all environments. If the complexity is something that is a deal-breaker for you, I can fork this so I have an implementation for Oli and we don't need to introduce this complexity to the original. If it's not a deal-breaker and you're willing to move forward, we can work together to find a way to minimize the overhead for you and other developers.
The packaging might seem daunting right now since you just spent a bunch of time downloading all the tools, setting up your environment, and reading through the internals. However, once this module is published to npm, it's very simple to use:
From the command line:
npm install --save badi-cal
In your code:
The package manager give the following to use for free:
In order to make things easier web developers with a simpler setup who just want a js file to drop into their project, we can deploy to a CDN, like surge, and provide a public link for them to access, then include that link in the documentation. (And we also provide builds for the browser within the repo for conventience as well).
npm run test
failingThere was a small bug I forgot to fix before pushing. It is fixed now and the tests pass. Sorry about that.