bchurchill / badi-cal

A javascript library for Badi calendar conversions.
https://bchurchill.github.io/badi-cal/
MIT License
11 stars 4 forks source link

Implements V2 of BadiDate library #12

Closed brenmcnamara closed 7 years ago

brenmcnamara commented 7 years ago
  1. On the topic of having the builds in the repository and having readable packaged code.

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.

  1. On the topic of publishing documentation and re-building badi-cal.js

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.

  1. On the topic of variable / function naming concerns

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.

  1. On the topic of missing documentation

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.

  1. On the topic of if / else vs ternary

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.

  1. Remove QUESTION from files

  2. 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"

  1. On the topic of combining functions into a single file

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.

  1. On the topic of camel case

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.

  1. On the topic of files with underscore

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.

  1. On the topic of long function names and 80 column lines

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.

  1. On the topic of added complexity

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.

  1. On the topic of packaging this project and ease of use

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:

import { BadiDate } from 'badi-cal';
// You may now use BadiDate 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).

  1. On the topic of npm run test failing

There was a small bug I forgot to fix before pushing. It is fixed now and the tests pass. Sorry about that.

bchurchill commented 7 years ago

Things to fix before merging:

  1. 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).

  2. 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.

  3. 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.

  1. 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).

  2. 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)
  1. Remove comments with "QUESTION". I've answered them below.

  2. 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:

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.

bchurchill commented 7 years ago

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.

bchurchill commented 7 years ago

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
bchurchill commented 7 years ago

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.

bchurchill commented 7 years ago

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:

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.

brenmcnamara commented 7 years ago
  1. Fixed documentation to use npm run create-build

  2. 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.

  1. On the topic of browser build v1 and browser build v2

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.

  1. On the topic of stale files in "build-v2"

Sorry about that, I cleaned it up. The process of how to clean the build is addresses in 5.

  1. I've added the command npm run clean-build and documented it in the README

  2. Renamed util.js to Astronomy.js

  3. Created a About the Code section and reordered some of the sections

  4. 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.

  1. On the topic of converting example2 to a unit test suite

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.

  1. On the topic of underscore vs camel case

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.

bchurchill commented 7 years ago

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:

bchurchill commented 7 years ago

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.