Retrieving Notes from iBook and Kindle

In my work as a fledgling author, I have reached the point of soliciting feedback from Beta Readers for my first novel, “Tishta the Crystal Orb.” It is the first in a series of fantasy novels—there will likely be five—and my first try at anything as large as this. It has been a fun adventure.

Today, I figured out how to retrieve Notes from iBook and Kindle. It was not a simple process and I still do not have methods for all platforms.

For iBooks, it was relatively painless on an iPhone, but not so much on a Mac (although, I did find one hacky way to do it). I wrote a blog post, in case anyone is interested.

On Kindle, it was just the opposite—relatively easy on the Mac, but not really possible on the iPhone, that I have been able to discover. I blogged about this, as well.

One reason for this post is to share what I learned.

The other is to ask, has anyone else figured out how to do this? Even with the methods I found and wrote about, it is going to be a big chore to match up the comments with the text. I am hoping to discover how to do this by the time my readers begin sending me their feedback. It would be nice if both of these formats did something similar to what can be done with .docx files.

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Advertisements
Posted in #Education, AmWriting, AmWritingFantasy, Books, Education, Fantasy, Life, Novel, Reading, Tishta, Writing | Tagged , , , , , | Leave a comment

Python and unittest

In this blog post, I will show how to create unit tests to go with some python code using unittest. The project will implement a simple stack using an array—or, more properly, a list.

Create a Project

Start by adding and navigating to your project folder, e.g., exercises. Initialize it with git so you can checkin your changes.

mkdir exercises
cd exercises
git init

Open project folder in your IDE.

Create a file, __init__.py, in exercises. Leave it empty. Save and close it.

Add a folder, src, in the project folder, exercises.
Create a file, __init__.py, in src. Leave it empty. Save and close it.
Create a file, stacks.py. Leave this empty, for now.

Add a folder, tests, in the project folder.
Create an init file, __init__.py.
Add this code:

import sys, os

myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, myPath + '/../src/')

Create a test file, test_stacks.py, and add this code:

import unittest

from stacks import Stack

class StackTestCase(unittest.TestCase):
    def test_is_empty_true(self):
        test_obj = Stack()
        self.assertEqual(True, test_obj.is_empty())

if __name__ == '__main__':
    unittest.main()

Run The Test

python -m unittest discover

This will generate the following:

E
======================================================================
ERROR: tests.test_stacks (unittest.loader.ModuleImportFailure)
----------------------------------------------------------------------
ImportError: Failed to import test module: tests.test_stacks
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 254, in _find_tests
    module = self._get_module_from_name(name)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/loader.py", line 232, in _get_module_from_name
    __import__(name)
  File "/Users/your_name/src/Closet/exercises/tests/test_stacks.py", line 3, in <module>
    from stacks import Stack
ImportError: No module named stacks

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

Of course, it fails. stacks does not exist. We have not yet coded it. I wrote the test first to show how extreme you can go with TDD (Test Driven Development). The advantage here is that you expect this type of failure, and you really test each and every step along the way. The first thing to do to get rid of this error is to create the class, Stack.

Add this to ./src/stacks.py:

class Stack(object):
    """docstring for Stack"""
    def __init__(self):
        super(Stack, self).__init__()

Run the test again:

python -m unittest discover

This time, the error is a little different:

E
======================================================================
ERROR: test_is_empty_true (tests.test_stacks.StackTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/your_name/src/Closet/exercises/tests/test_stacks.py", line 8, in test_is_empty_true
    self.assertEqual(True, test_obj.is_empty())
AttributeError: 'Stack' object has no attribute 'is_empty'

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (errors=1)

Again, a very expected error. Add the method, is_empty() to fix it.

    def is_empty(self):
        return self.stack == []

Run the test and see it work:

.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK

Notice the E at the beginning has changed to a dot. A dot indicates a successful test. Also, FAILED (errors=1) has become OK.

Add A Second Test

We are missing a test. We should test for is_empty() is False. By adding this test, we will be forced to implement push() to make it pass.
Here’s the test:

    def test_is_empty_false(self):
        test_obj = Stack()
        test_obj.push('data')
        self.assertEqual(False, test_obj.is_empty())

Here’s the failure:

E.
======================================================================
ERROR: test_is_empty_false (tests.test_stacks.StackTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/ramonaridgewell/src/Closet/Udemy Python Algorithms/exercises/tests/test_stacks.py", line 12, in test_is_empty_false
    test_obj.push('data')
AttributeError: 'Stack' object has no attribute 'push'

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=1)

Here’s the code to fix it:

    def push(self, data):
        self.stack.append(data)

As expected, the tests both now pass:

..
----------------------------------------------------------------------
Ran 2 tests in 0.000s

OK

This is probably a good spot to check in the code. Add a new file, .gitignore in the project directory. Add this:

.DS_Store
*.pyc

This code will tell git to ignore the Desktop Services Store and all the python compiled source files—there is no reason to check these in. Since folders are being committed on this checkin, you can’t see the files with their extensions. Run:

git add .

Then run:

git st

before the checkin to ensure only the files you want to check in are listed. When you are satisfied, run:

git commit -m "initial checkin - added is_empty() and push() to new Stack class"

I’m not going to explain creating a github repo here. If you need to know how to do that, check out this blog post for instructions.

What Comes Next?

Now is a good time to think about what other tests you can add to ensure the current codebase is correct. With only push() and is_empty(), there aren’t many interesting things we can test. We could push on a second item, but we can’t really test anything except that is_empty() is still True. I think adding tests around pop() would be a more interesting and efficient use of our time. Let’s write a test.

    def test_pop_handles_empty_stack(self):
        test_obj = Stack()
        self.assertEqual(None, test_obj.pop())
        self.assertEqual(True, test_obj.is_empty())

I started with this code:

    def pop(self):
        data = self.stack[-1]
        del self.stack[-1]
        return data

but it failed with:

Traceback (most recent call last):
  File "/Users/ramonaridgewell/src/Closet/Udemy Python Algorithms/exercises/tests/test_stacks.py", line 17, in test_pop_removes_item_from_stack
    self.assertEqual(None, test_obj.pop())
  File "/Users/your_name/src/Closet/exercises/tests/../src/stacks.py", line 14, in pop
    data = self.stack[-1]
IndexError: list index out of range

----------------------------------------------------------------------
Ran 3 tests in 0.000s

FAILED (errors=1)

Nice boundary condition test. Put is a is_empty() check to solve the problem and make the test green.

    def pop(self):
        if self.is_empty():
            return None

        data = self.stack[-1]
        del self.stack[-1]
        return data

Let’s add a few more tests:

    def test_pop_pops_1_item_stack(self):
        test_obj = Stack()
        test_data = 'data1'
        test_obj.push(test_data)
        self.assertEqual(test_data, test_obj.pop())
        self.assertEqual(True, test_obj.is_empty())

    def test_pop_in_correct_order_from_2_item_stack(self):
        test_obj = Stack()
        test_data1 = 'data1'
        test_data2 = 'data2'
        test_obj.push(test_data1)
        test_obj.push(test_data2)
        self.assertEqual(test_data2, test_obj.pop())
        self.assertEqual(False, test_obj.is_empty())
        self.assertEqual(test_data1, test_obj.pop())
        self.assertEqual(True, test_obj.is_empty())

These both pass. Check in the code.

Another Method and More Tests

The other typical method on a stack is peek(), which displays the most recent item to be pushed onto the stack (or the top of the stack). It should be very similar to pop() except it won’t remove the item from the stack, simply return the value. Here are three tests to go with it:

    def test_peek_handles_empty_stack(self):
        test_obj = Stack()
        self.assertEqual(None, test_obj.peek())
        self.assertEqual(True, test_obj.is_empty())

    def test_peek_shows_item_in_1_item_stack(self):
        test_obj = Stack()
        test_data = 'data1'
        test_obj.push(test_data)
        self.assertEqual(test_data, test_obj.peek())
        self.assertEqual(False, test_obj.is_empty())

    def test_peek_shows_top_of_2_item_stack(self):
        test_obj = Stack()
        test_data1 = 'data1'
        test_data2 = 'data2'
        test_obj.push(test_data1)
        test_obj.push(test_data2)
        self.assertEqual(test_data2, test_obj.peek())
        self.assertEqual(False, test_obj.is_empty())
        self.assertEqual(test_data2, test_obj.peek())

and the new method:

    def peek(self):
        if self.is_empty():
            return None

        return self.stack[-1]

That’s it for peek(). Time to check in the code.

One Last Method

To round out our stack, we should add a size() method. Add the tests:

    def test_size_handles_empty_stack(self):
        test_obj = Stack()
        self.assertEqual(0, test_obj.size())
        self.assertEqual(True, test_obj.is_empty())

    def test_size_returns_1_for_1_item_stack(self):
        test_obj = Stack()
        test_data = 'data1'
        test_obj.push(test_data)
        self.assertEqual(1, test_obj.size())
        self.assertEqual(False, test_obj.is_empty())

    def test_size_returns_correct_number_for_many_item_stack(self):
        test_obj = Stack()
        test_data0 = 'data0'
        test_data1 = 'data1'
        test_data2 = 'data2'
        test_data3 = 'data3'
        test_data4 = 'data4'
        test_data5 = 'data5'
        test_data6 = 'data6'
        test_data7 = 'data7'
        test_data8 = 'data8'
        test_data9 = 'data9'
        test_obj.push(test_data0)
        test_obj.push(test_data1)
        test_obj.push(test_data2)
        test_obj.push(test_data3)
        test_obj.push(test_data4)
        test_obj.push(test_data5)
        test_obj.push(test_data6)
        test_obj.push(test_data7)
        test_obj.push(test_data8)
        test_obj.push(test_data9)
        self.assertEqual(10, test_obj.size())

and the method:

    def size(self):
        return len(self.stack)

Be aware of what you might be missing when you review your test cases. It was interesting to me that we didn’t write any specific tests for push()—it can’t really be tested all by itself. is_empty(), pop() and peek() tests all utilize push(). I believe it has been completely tested in this suite of tests.

That’s it for our little stack. Check in your code and let’s have a beer.

In Conclusion

This is really simple example. I hope you can find some value in learning to unit test as you write your code. As is usually the case, there is way more test code than production code. There always is. The more complex the code, the larger the ratio will become. Tests give developers the confidence to change their code, and know they haven’t broken anything in the process. Tests are your friends—I guarantee it. When I choose not to write tests—and, for me, it’s a carefully considered choice—I always seem to regret it later. I write bugs—everybody does. Tests help ensure there are fewer of them.

You can find this code in my repository, python-stack-w-unittest.

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Posted in #Education, Coding, GitHub, Programming, Python, Science, STEM | Tagged , , , , , , , , , | Leave a comment

How Insane is That?

This started out as a response to comment made on an Instagram post by Thomas Dekker, whose hometown is Las Vegas. I was not planning to blog about the Route 91 Harvest Festival shootings. It hit too close to home for me when I found out a close friend’s cousin was among the murdered… slaughtered… sacrificed? Thomas posted a photo of a bumper sticker promoting the NRA, with the comment, “Could not be a more unpleasant time to pull up next to a car with this sticker on it. #stopthemadness” The comment that got me going was: “Why? How do you protect yourself? Many of us do not have armed guards to protect us or live in gated community. Read about the history of the Constitution. Check out what the NRA is about. Do not be a sheep. Find out for yourself.”  What follows was my response that comment—it was too long for Instagram to accept. Since I had already spent the time on it, I decided to write a blog post, instead.

It got me going. Here’s a little of the history of the Constitution, The Second Amendment, what a militia is and was, the US Army, the Revolutionary War.

Just as a disclaimer—I grew up rurally—I know how it feels to be out in the middle of nowhere, alone, in the middle of the night. I am fully capable of using rifles and firearms (I’m also a pretty good shot). I am aware that in a home invasion, a good old shotgun is your best defense.

Another side of includes: my best friend lost one of his cousins in Las Vegas last weekend.

I try to be impartial—I understand both views—but I believe we are, collectively, insane to allow the gun violence in our country to continue unabated.

At the time the Bill of Rights was added to the Constitution, in 1791, we had no standing army. Because of “the republican distrust of standing armies,” the Continental Army of the Revolutionary War was immediately disbanded after the war was over. At that point, the states had militias as protection (which were regulated by Congress). These are the militia referred to by “a well regulated militia being necessary to the security of a free state.” As the US Army (which was not formed until 1791, when the Feds realized they could not eradicate the Indigenous People without a real army) began to grow, the state militias shrunk, and, by 1903, were redefined via the Militia Act of 1903 to “organized militia,” which includes state-level National Guard and Naval Militia; and “unorganized militia,” which is “every able-bodied man of at least 17 and under 45 years of age not a member of one of the organized militia”—notice the term “well regulated” was not included.

The 2nd Amendment is fairly ambiguously stated—as the Founders likely intended—so it could be interpreted according to the needs of the current time and populace.

In the 20th and 21st centuries, we have allowed special interest groups (you can read that as groups, like the NRA, supported by people who makes lots of money from their interests—this would be the weapons and ammunition manufacturing lobby—and are willing to spend vast sums to ensure they can continue to make lots of money) to sway the current, legal interpretation to include nearly any conceivable weapon—not only ones intended for self-protection or hunting, but ones intended for killing a lot of people at once. In my opinion, no one should be allowed to own assault rifles. Ammunition should be restricted—for instance, none that pierce body armor should be allowed.

There is nothing in The Second Amendment that prohibits the weapons from be regulated—or the operators of those weapons. The weapons and their operators should be licensed—similar to cars. A lost or stolen weapon would have to reported, tracked and located. An operator should lose his license—and his right to own guns—if he breaks the rules. Only by a special collectors license should an operator be allowed to own more than <some number> of guns or otherwise-banned weapons.

These are rational limitations on gun ownership. Other countries have implemented rules like these—Australia is a very good modern example, where they passed strict limitations even though our own NRA lobbied aggressively there to block the ban. They no longer have mass shootings—their last one was on April 28, 1996, and finally spurred their government into taking action. Their overall gun deaths per year dropped to 0.93 per 100,000—around 3 is the max for all other “first world countries” and most emerging nations. We have 10.54 per 100,000 per year—on par with Uruguay, and 30% higher than places like Mexico, Argentina and Paraguay. Only ten countries have higher rates than The US, including Brazil, Columbia and Venezuela [From Wiki—mixed most-recent years available]

So far this year, there have been 11,793 gun deaths in 47,142 gun-related incidents—which went up by 12 and 31, respectively, since I started writing this about an hour ago. This does not include the roughly 22,000 suicides by gunshot that occur each year. We will end the year around 35,000 gun deaths—this is “normal,” for us—and, we have normalized it. We have the highest gun ownership rate in the world, although, it is impossible to know how many guns are out there because the FBI only tracks applications, not actual purchases—how insane is that?

Guns kill people. It’s really their only purpose. If people didn’t have guns, no one would die from gunshot wounds. #StopTheMadness

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Posted in Gun Violence, Las Vegas, Las Vegas Shootings, Life, Murder, NRA, Polarized America, Route 91, Route 91 Harvest Festival, Route 91 Harvest Festival shootings, Thomas Dekker | Tagged , , , , , , , , , , | Leave a comment

My First Literary Rejection

Yay! I got my first rejection, from Bards and Sages Publishing, for “Once There Were Men.” I have not had time to write much in terms of short stories, so this was my first submission. I got much appreciated feedback. Very excited!

They gave me a scorecard:
Category Score
Character Development 
Dialogue
Grammar/Punctuation 4
Narrative Voice 3
Originality 2
Plot 2
Point-of-View
World-Building
Final Score 2.750

It’s probably a pretty bad score, but now I know more of what they are looking for. Bards and Sages left a comment on The Wolf Dreams Books Facebook page: “It is always nice when someone takes a rejection in stride. If you are interested, we provide a breakdown of what those scores actually mean on the website.”

I also received Reviewer Notes: “To me, this reads less as a story and more as a brief lecture on the overall morality of the human species, or, in the author’s apparent view, the lack of it. The species is presented as callous and selfish in the extreme, but without any detail or nuance to the presentation. I actually have a difficult time classifying this as fiction rather than opinion as there’s little in the piece designed to make me feel rather than tell me how horrible my species is.”

After “Tishta the Crystal Orb” is out, I’m planning to focus on developing my short story skills. 2018 will be a year of discovering places to submit and how to write the stories they want to read.

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Posted in AmWriting, AmWritingFantasy, Books, Environment, Fantasy, Horror, Life, Novel, Reading, Science, Writing | Tagged , , , , , , , | 1 Comment

The Wrong Donations – Some Tough Words on Disaster Relief

This is a heartfelt reminder to think before you donate goods. It applies to any donation you might make to any charity–local, national or international. The work it takes to sort through donations of unusable clothing and other items sometimes is not worth the effort. In general, underwear and socks are always in high demand–but donate NEW ONES. Any other clothing donations should be clean and gently used (or, better yet, new or like-new).

My Best Laid Plans

I need to make a statement. I want to say it as kindly and gently as possible, but this message really needs to get out there. It’s important. Please hear me with as much grace as you can, because I mean it with all love and gentleness.

My children and I spent hours yesterday sorting the donations that are pouring in. That picture is the mountain we were faced with, and it was still coming. We’re not the only ones. Hundreds (thousands?) of volunteers all across our state are doing the same exact thing. Why? Because your hearts are in theright place.That’s why.

I want to make that abundantly clear. It is beautifully apparent that you are thinking about us and that you want to help us figure this thing out. You are doing anything you can, and that has brought such profound joy to our hearts. I personally…

View original post 1,093 more words

Posted in Uncategorized | Leave a comment

My JS Journey: Creating an npm Package

In this blog post, I will show how to create an npm package that can be used from other modules. The example is a wrapper around node fs.

Setting up

Start as you would for any ES6 project by creating the following. The steps are covered in previous blog postings: My JavaScript Journey: Basic ES6 CLI Skeleton and Mocha as a JavaScript Test Framework.

mkdir file-handler
cd file-handler
git init
npm init -y
mkdir src
cd src
create index.js
create _index.js
cd ..
mkdir test
cd test
create test.js
cd ..
npm install babel-core --save-dev
npm install babel-preset-env --save-dev
npm install babel-register --save-dev
npm install babel-cli --save-dev
npm install mocha --save-dev
create .gitignore
create .babelrc
cd src
update package.json to add scripts, etc.

Test fs.readFile

Add tests to test/test.js that call your wrapper method

import assert from 'assert';
import FileHandler from '../src/index';

describe('FileHandler', function() {
  const testFilesPath = 'test/data/';
  const fileName = 'hello.txt';
  let fileHandler = new FileHandler();
  describe('getData', function() {
    it('should return error when invalid path', function(done) {
      const path = "blah";
      const expected = "ENOENT: no such file or directory, open '" + path + "'";
      fileHandler.getData(path)
      .catch(function (e) {
        assert.equal(expected, e.message);
        done();
      })
      .catch(function(err) {
        done(err);
      });
    });

    it('should return file contents', function(done) {
      const expected = "hello";
      fileHandler.getData(`${testFilesPath}${fileName}`)
      .then(function(data) {
        assert.equal(encodeURI(data), expected);
        done();
      })
      .catch(function(err) {
        done(err);
      });
    });
  });
});

Add a wrapper method to src/index.js

Since the only thing you will be exporting is the FileHandler class, the code goes directly into /src/index.js. This is what will be imported by the module that uses it.

import fs from 'fs';

class FileHandler {
  getData(path, type = 'utf8') {
    return new Promise((resolve, reject) => {
      fs.readFile(path, type, (err, data) => {
        if (err) { reject(err); }
        resolve(data);
      })
    });
  }
}

export default FileHandler;

Run the test

Add a script to run the tests to package.json:

"test": "mocha --compilers js:babel-core/register test/test.js"

Run:
npm test

One test should pass and one should fail.

  FileHandler
    getData
      ✓ should return error when invalid path
      1) should return file contents

  1 passing (268ms)
  1 failing

  1) FileHandler getData should return file contents:
     Error: ENOENT: no such file or directory, open 'test/data/hello.txt'
      at Error (native)

To make the tests run automatically with each change, add the following scripts to package.json:

"start": "npm run dev",
"dev": "npm test -- -w",

To ensure you build after each change, add the following script to package.json ("pre" is a prefix that will run the script before either build or test):

"pretest": "npm run build",

And, to prevent the test file(s) from being included in the distribution, add the babel-cli flag --ignore to the build script in package.json:

"build": "babel ./src -d ./dist --ignore test.js",

Now, run:

npm start

You will see the scripts chained together. In the end, Mocha will be running, waiting for the next change to be saved. To quit, ctrl-c.


> file-handler@1.0.0 start /Users/<your source>/file-handler
> npm run dev

> file-handler@1.0.0 dev /Users/<your source>/file-handler
> npm test -- -w

> file-handler@1.0.0 pretest /Users/<your source>/file-handler
> npm run build

> file-handler@1.0.0 prebuild /Users/<your source>/file-handler
> npm run clean

> file-handler@1.0.0 clean /Users/<your source>/file-handler
> rm -rf dist

> file-handler@1.0.0 build /Users/<your source>/file-handler
> babel ./src -d ./dist --ignore test.js

src/_index.js -> dist/_index.js
src/index.js -> dist/index.js

> file-handler@1.0.0 test /Users/<your source>/file-handler
> mocha --compilers js:babel-core/register test/test.js "-w"

  FileHandler
    getData
      ✓ should return error when invalid path
      1) should return file contents

  1 passing (268ms)
  1 failing

  1) FileHandler getData should return file contents:
     Error: ENOENT: no such file or directory, open 'test/data/hello.txt'
      at Error (native)

Add test/hello.txt File

cd test
mkdir data
create hello.txt

You will need to tickle src/index.js and save it to trigger the tests to run again (or ctrl-c and start again). The test will still fail since there is no content in test/data/hello.txt.

  FileHandler
    getData
      ✓ should return error when invalid path
      1) should return file contents

  1 passing (39ms)
  1 failing

  1) FileHandler getData should return file contents:

      AssertionError: '' == 'hello'
      + expected - actual

      +hello

      at test/test.js:26:16

Add ‘hello’ to test/data/hello.txt. Tickle index.js and save it to run the tests again. All happy and green now.

  FileHandler
    getData
      ✓ should return error when invalid path
      ✓ should return file contents

  2 passing (8ms)

Checkin

This is a good spot to checkin.

Currently, my package.json looks like this:

{
  "name": "file-handler",
  "version": "1.0.0",
  "description": "Library creation demonstration",
  "main": "index.js",
  "scripts": {
    "clean": "rm -rf dist",
    "prebuild": "npm run clean",
    "build": "babel ./src -d ./dist --ignore test.js",
    "start": "npm run dev",
    "dev": "npm test -- -w",
    "pretest": "npm run build",
    "test": "mocha --compilers js:babel-core/register test/test.js"
  },
  "keywords": [
    "node",
    "fs",
    "testing",
    "mocha",
    "javascript",
    "es6"
  ],
  "author": "Ramona Ridgewell",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.25.0",
    "babel-preset-env": "^1.5.2",
    "babel-register": "^6.24.1",
    "mocha": "^3.4.2"
  }
}

Using the Library

We need a different app to import file-handler. How do we do that? First, to get the ES5 transpiled version of the code, change the "main" property in package.json to point to ./dist/index.js.

  "main": "./dist/index.js",

The final bit is to set which files are required by the end-user, which is the ./dist/ folder. Add this to package.json:

"files": [
  "dist"
]

Create node.js Package Module

The first step in publishing an npm module is to create the package. Navigate to the root where the package.json is. Run:

npm pack

This creates file-handler-1.0.0.tgz in the same directory. It gets the "name" field from package.json and appends the "version" from the same file, to create the file name. You can see the contents by running:

tar -tf file-handler-1.0.0.tgz

which returns:

package/package.json
package/README.md
package/dist/_index.js
package/dist/index.js

Test the Module

Create Test Module

To set up a new module, you can use this package.json or however you wish to set it up.

{
  "name": "testFileLib",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "clean": "rm -rf dist",
    "prebuild": "npm run clean",
    "build": "babel ./src -d ./dist --ignore test.js",
    "start": "npm run dev",
    "dev": "npm test -- -w",
    "pretest": "npm run build",
    "test": "mocha --compilers js:babel-core/register test/test.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-core": "^6.25.0",
    "babel-preset-env": "^1.5.2",
    "babel-register": "^6.24.1",
    "mocha": "^3.4.2"
  }
}

Add the usual src/index.js, src/_index.js, .babelrc, .gitignore. index.js doesn’t need to do anything at this point.

const main = () => {
  console.log("hello");
};

main();

Install Your Package

Here’s the most exciting part. Navigate to the root of your test project and run:

npm install ../file-handler/file-handler-1.0.0.tgz

The path assumes your library and test project are in the same parent directory —adjust appropriately. The output looks like this:

testFileLib@1.0.0 /Users/<your source>/testFileLib
└── file-handler@1.0.0

npm WARN testFileLib@1.0.0 No repository field.

If you check ./node_modules/, you will find a file-handler directory with the built /dist/, and the package.json and README.md.

Write Some Tests

I literally copied the tests from file-handler and changed line 2 to:

import FileHandler from 'file-handler';

Running npm test should just work as expected.

Read File When Running Test App

To use file-handler in index.js, change it to the following:

import FileHandler from 'file-handler';

const main = () => {
  const fileHandler = new FileHandler();
  const fileName = "test/data/hello.txt"
  fileHandler.getData(fileName)
  .then((data) => {
      console.log(data);
  })
  .catch((err) => {
    console.log("error reading file", err);
  });
};

main();

If you run:

npm test

it will clean, build and run your tests. Then, run:

node src/_index

It should say hello or whatever you put in your file.

Add file-handler to npm

At this point, even though the library only supports one method, I am going to show how to make your new library available on npm.

Add User

Assuming you have never done this before, run the following:

npm adduser

It will prompt you for Username, Password, Email. Username must be unique, so you might need to try a couple of times to find an unused one. The error begins with this:

npm WARN adduser Incorrect username or password
npm WARN adduser You can reset your account by visiting:
npm WARN adduser
npm WARN adduser     https://npmjs.org/forgot
npm WARN adduser

You can verify you are not already using the email by going to the link https://npmjs.org/forgot and trying to have a reset email sent. When you give it all valid responses, it will respond with:

Logged in as on https://registry.npmjs.org/.

Confirm you were added by going to:

https://www.npmjs.com/~<your username>

If you already have an npm user, you can login using:

npm login

Publish the Module

Now, it’s time to publish. As long as you’re in the root of the project (with the package.json), you don’t need to specify the folder to point to that file. Include the tarball and, unless you’re a paying member, don’t forget to make the package public (it defaults to private).

npm publish file-handler-1.0.0.tgz --access public

If your project name is already taken on npm, which this example assumes, you will receive an obtuse error message that includes:

npm ERR! you do not have permission to publish "file-handler". Are you logged in as the correct user? : file-handler

To overcome the error without simply choosing a different package name, create and install a scoped package, which I discuss below. I think this is a better way to go since all of your packages will be bundled under your npm user name in the file hierarchy.
If you wish, you can search to try to find a unique name using:

npm search

To add scope, prefix your package.json "name" with @ followed by your npm user name. The @ is important and will be included in the import paths.

{
  "name": "@<your npm user name>/file-handler"
}

Create a new tarball. It will include the prefix.

npm pack

generates the file

<your npm user name>-file-handler-1.0.0.tgz

When you install the new tarball in your test project, you will find this folder structure in ./node_modules/:

/@<your npm user name>/file-handler/

Of course, you will then need to change your test project code paths to reflect the change. Don’t forget the @.

import FileHandler from '@<your npm user name>/file-handler';

Run your tests. They should just pass.

This time, publishing should work:

npm publish <your npm user name>-file-handler-1.0.0.tgz --access public

A successful publish will result in something like this:

+ @<your npm user name>/file-handler@1.0.0

Once you publish a package with a given name and version, it can never be used again. You will need to change the version in package.json.

Now, go back to your test project’s ./node_modules/ and delete

./@<your npm user name>/file-handler/

Then, to install your package from npm, run:

npm install @<your npm user name>/file-handler --save-dev

It should succeed. The output looks like this:

testFileLib@1.0.0 /Users/<your>/<src>/testFileLib
└── @<your npm user name>/file-handler@1.0.0

npm WARN testFileLib@1.0.0 No repository field.

If you check your package.json, you should find this line added to the devDependencies:

"@<your npm user name>/file-handler": "^1.0.0",

Run your tests. All should be well in the universe.

In Conclusion

Wow. This took a lot of research. In my next blog post, I will cover making updates to the npm package. The file-handler code is in my GitHub file-handler repository.

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Posted in #Education, #npm, AmWriting, Coding, GitHub, JavaScript, Programming, Science, STEM | Tagged , , , , , , , , , , , , , , | 1 Comment

Mocha as a JavaScript Test Framework

The last few projects I discussed were to get you started coding in ES6 JavaScript. I intentionally kept the functionality very limited. In this article, I will show how to set up a test framework using my ES6 CLI Promises repository, promises. There are many different frameworks to choose from. A simple one to set up is Mocha.

Setting up

First, clone promises (the instructions for cloning are included in the article that goes with this repository, My JS Journey: Promises).

Next, install the node dependencies.

npm install

Then, build and run it to ensure everything is set up correctly.

npm run build
npm start

It should say,
hello world

Install Mocha

Install Mocha.

npm install mocha --save-dev

This should have added mocha to the devDependencies in package.json. Now is a good time to change the name.

{
  "name": "MochaExample",
  "version": "1.0.0",
  "description": "Simple Mocha example",
  "main": "index.js",
  "scripts": {
    "build": "babel src -d dist",
    "start": "node dist/index",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.24.0",
    "babel-core": "^6.24.0",
    "babel-preset-env": "^1.3.2",
    "babel-register": "^6.24.0",
    "mocha": "^3.3.0"
  }
}

Might as well check in the code.

Convert src/hello.js to Implement a class

To facilitate exporting, I find it easiest to use classes. Modify src/hello.js as follows.

class Hello {
  static getWorld() {
    return new Promise((resolve) => {
      setTimeout(() => resolve(' world'), 500);
    });
  }

  static hello() {
    return new Promise((resolve) => {
      this.getWorld().then(result => {
        resolve('hello' + result);
      });
    });
  }
}
export default Hello;

Notice that the functions changed from const to static. Also, now that they are part of the object, to reference them within the object, it is necessary to prefix them with this., e.g. this.getWorld(), as in line 10.
Modify src/index.js to accommodate the new class.

import Hello from './hello';

const sayHello = (message) => {
  console.log(message);
};

const main = () => {
    var args = process.argv;
    if (args[2] && args[2] === '--v' ) {
        console.log('Es6CliSkeleton - version 1.0.0');
    } else {
        Hello.hello().then(res => sayHello(res));
    }
};

main();

The lines that changed are 1 and 12, both of which reference the new Hello class.

Build and run to ensure everything still works.

Adding Tests

First, create a folder test. Add a file to this folder called hello_test.js.
Add the first test.

import assert from 'assert';
import Hello from '../src/hello';

describe('Hello', function() {
  describe('getWorld', function() {
    it('should return space world', function() {
      Hello.getWorld()
      .then(result => {
        assert.equal(' world', result);
      });
    });
  });
});

Next, in package.json, modify the “scripts” section’s “test” to:

"test": "mocha --compilers js:babel-core/register test/test.js"

Build and run the test.

npm run build
npm run test

You should get output something like this.

> MochaExample@1.0.0 test /Users/your_src/promises
> mocha --compilers js:babel-core/register test/*_test.js
  Hello
    getWorld
      ✓ should return space world
  1 passing (100ms)

With a nice green checkmark.
Check the code in.

A Second Test

We should have a test on the other function, Hello.hello(). It will be very similar the first one. Add this right code after the other test.

  describe('hello', function() {
    it('should return hello world', function() {
      Hello.hello()
      .then(result => {
        assert.equal('hello world', result);
      });
    });
  });

Build and run. The output should look like this.

  Hello
    getWorld
      ✓ should return space world
    hello
      ✓ should return hello world

  2 passing (65ms)

Go ahead and check that in since the next section covers something a little different.

A Broken Promise

Let’s see what happens when the promise is broken. Modify what the promise returns in Hello.getWorld() from

  setTimeout(() => resolve(' world'), 500);

to

  let input = "myErr";
  if (input === "myErr") { reject(new Error(input)); }
  setTimeout(() => resolve(' world'), 500);

Build and run the app.

> MochaExample@1.0.0 start /Users/your_src/Closet/promises
> node dist/index

(node:61097) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): 42

It fails, kind of how you might expect. Run the tests.

  Hello
    getWorld
      ✓ should return space world
(node:61087) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: myErr
    hello
      ✓ should return hello world
(node:61087) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 4): Error: myErr

  2 passing (111ms)

Interestingly, the tests pass, although they report the unhandled promise rejection. Let’s fix that first. Modify the tests to include a catch, which will also require the use of done in lines 6, 10 and 14, and 19, 23 and 27.

import assert from 'assert';
import Hello from '../src/hello';

describe('Hello', function() {
  describe('getWorld', function() {
    it('should return space world', function(done) {
      Hello.getWorld()
      .then(result => {
        assert.equal(' world', result);
        done();
      })
      .catch(err => {
        console.log(err);
        done(err);
      });
    });
  });
  describe('hello', function() {
    it('should return hello world', function(done) {
      Hello.hello()
      .then(result => {
        assert.equal('hello world', result);
        done();
      })
      .catch(err => {
        console.log(err);
        done(err);
      });
    });
  });
});

Now, the tests fail and let you know why:

  1) Hello getWorld should return space world:
     Error: myErr
      at src/hello.js:5:39
      at Function.getWorld (src/hello.js:3:12)
      at Context.<anonymous> (test/hello_test.js:7:13)

  2) Hello hello should return hello world:
     Error: myErr
      at src/hello.js:5:39
      at Function.getWorld (src/hello.js:3:12)
      at src/hello.js:12:12
      at Function.hello (src/hello.js:11:12)
      at Context.<anonymous> (test/hello_test.js:20:13)

Remove the changes to getWorld(). Check in the code.

In Conclusion

I generally start by writing my tests (Test Driven Development or TDD). I believe it is one of the fundamental practices in good code development. It is important to ensure you have complete test coverage. In this example, you can see I had tests for the “happy path” but had not covered potential failures. It was a bit contrived, but be on the lookout for things that can go wrong. In My JS Journey: Creating an npm Package, I show how to wrap Node fs, which is their file system interface. That blog post includes more about how to use Mocha in a less-contrived project. As that project progresses, it will have a lot of good examples where you need to test for things that might go wrong—a missing file, an empty file, invalid permissions—lots of things to test for. It also has

You can find the code for this project in my github repository, mocha-example.

Copyright ©2014-17 Ramona Ridgewell. All rights reserved.

Posted in #Education, AmWriting, Education, GitHub, JavaScript, Programming, Science, Software Development, STEM | Tagged , , , , , , , , , , | 1 Comment