Node Express Mustache Template Caching

In my class, we are teaching the students how to use Mustache templates as the view engine for their express application. Well, one problem that I was running into was that the server needed to restart every time I modified a template.

So I googled the problem and didn’t find a solution immediately. Then, I went back to the readme and noticed this.

Properties

The return function has a cache parameter that is an LRU Cache.

So I figured that I would just try this.

And it worked! I can make changes to my mustache templates and just refresh the browser. Be sure not to do this in production. Check your environment before disabling cache.

Until next time!

You Say Goodbye and I Say Hello

Some of you might know that toward the beginning of the year I took a job as an instructor at a company called The Iron Yard at their Dallas campus. They are a coding boot camp for adults who aspire to enter the software development industry. After spending 16 years in software development, I decided that I wanted to help the next generation of software developers. Teaching and watching other people learn how to develop software has taught me so much. It has been some of the hardest work I have ever done, but it has been the most rewarding.

Unfortunately, this past week I have been informed that The Iron Yard is closing all campuses after the current cohorts finish. I am very saddened by this news. Our campus has an excellent team. We can learn from each other without feeling the need to defend ourselves. Every situation has always been about how can we best handle this to help our students the most. And, really our students was what this was all about. Our students have always been our top priority.

I’m going to stay with The Iron Yard until the end because to do anything else would be damaging to our students. I am weighing several options on how I want to move forward. Teaching is where I want to stay for now. Keep an eye on this blog. I plan on posting a lot more posts and videos as time goes on. I want to document the process of going through this transition.

Until next post, see you later.

LXC, Docker, IPTables, and port forwarding

Lately I have been working on more devops. I ran into a situation where I have created an LXC container but already had Docker installed and running on my host. I ran into trouble when I wanted to forward a port from the host to the container. The way you would do this is to create a new NAT rule using iptables to forward a port from the host to the container.

Disclaimer: If you don’t have physical access to your box be very careful. You can totally lock yourself out of your machine if you do the wrong thing.

Every article on the internet would show something like the command below.

This command would append a new rule to the NAT table to forward incoming traffic from 8080 on the host to port 80 in the container. Well what I didn’t realize is that Docker already had a rule in the first position. The rule that docker adds basically catches all traffic. IPTables runs through the rule list from top to bottom. So what I had to do was run the following.

This inserts my IPTables rule in the first position. After I did this everything started working.

Use this command to show your current NAT rules.

Be sure to run iptables-save once you are done modifying your rules.

Meteor JS (Part 7): Item Search

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

If you would like to follow along I have tagged the git repository on Github at the starting point for this post. Here is the URL to the tag

https://github.com/dhirshjr/pricebook/releases/tag/v0.6.0

In this post we are going to add the email address of the currently logged in user to our layout. Then we are going add an item search feature. The way the search feature will work is by letting you enter a search term for each column. This will allow us to limit the number of records coming back.

So let’s get started.

Logged In User Email Address

In the last post we discussed authentication and authorization. We added a sign in button but forgot something very important. How do we know which user is logged in? We don’t. Thanks goes out to AlanB for calling this one out.

Javascript

First let’s create a UI helper that we can call from any template. This will allow us to place {{currentUserName}} anywhere we need to display the username.

Add the following code to the end of client/lib/helpers.js.

UI

Now let’s use the UI helper in our layout template.

Add the following to client/views/layouts/layout.html.

After you save you should start to see the email address of the currently logged in user, beside the sign out button, if you are signed in.

Item Search

We will be placing a search button in the header of the first column. When a user clicks the search button a filter field will appear in each column. All string type fields will use a regular expression search. All number type fields will use an exact search. For the query, all fields that are filled in will be combined together using an and operator. Below is an image of he finished product.

MeteorJSPart7FinshedProduct

Item Search Template

First, we are going to create a template to handle filtering a column. When we use this template we will use the following syntax.

Let’s create a file at client/views/items/itemsearch.html and add the following code.

Nothing really that interesting here other than value getting assigned from searchValue. We will get to see where that comes from when we get to itemsearch.js.

Next, let’s create a file at client/views/items/itemsearch.js and add the following code.

There is a lot going on in here. We are attaching an event on the key up of the .searchInput within the current template. Then, we are throttling it to only fire once every half second.

The event handler itself is taking in the event and template as arguments. Within the handler, we are checking for our session object. If one doesn’t exist we create an empty object. Next, we assign searchValue the value from the .searchInput within the template.

At this point we need to check if we got an empty string or not. If we didn’t get an empty string we need to look at our number parameter on the template. If it is a number just parse it as a number. If it isn’t a number just assign the string value of the input to our searchQuery object.

Now here is where things get interesting. At the point that the property is created it will always be on the searchQuery object. We need to get rid of empty properties because we don’t want to burden the server with doing a search on empty strings. So we delete the property by using the delete keyword.

Finally, we save the searchQuery to our session. What this event handler allows us to do is place as many item search templates as we have columns in our items collection.

All that is left is to create the helper that returns the value. This allows the item search template to survive a hot code push because session is preserved when a hot code push happens.

Finally let’s create a file at client/views/items/itemsearch.css and add the following code.

Here we are just saying hide the inputs by default. You might be wondering why I have used so many classes to define this rule? Originally I tried just using my filterInput class but Semantic UI had rules that were more specific than mine. In order to override them I had to create a rule with higher specificity. If you would like to know more about CSS specificity check out my post on the subject.

CSS: Specificity

Item Publication

Everything we have done so far has been on the client. We need to modify the allItems publication on the server side to take in our searchQuery object that we create on the client side. This is where the true beauty of Meteor starts to shine. All I have done on the client side is create an object in Javascript. And I just need to use it on the server side.

Modify server/publications/items/item.js by replacing the code with the following.

All we are doing here is just looping the properties of the searchQuery object and transforming them into a Mongo DB query. What is really cool about this is that it makes only the search results available to the UI. You will notice, in the next section, that we are not going to make any changes to how the list of items is generated. This is part of the reactive nature or Meteor JS.

Item List

Next, let’s modify client/views/items/itemlist.js by adding the following code. You will notice at this point that we have moved our subscription within a call to Tracker.autorun. This allows the subscription to update every time the Session searchQuery object changes. At the bottom we are adding an event handler to make our filter inputs appear.

All we need to now is to modify client/views/items/itemlist.html to use our new itemSearch template by adding the following code.

Finally, let’s create a  file at client/views/items/itemlist.css and add the following code. This just changes the cursor to a pointer on our search button.

Summary

In this post we added a pretty sophisticated search mechanism. There are better ways of doing search. Like using a Mongo DB fulltext index. But this is all we need for now. I may visit better ways of searching in a later post.

If you have any questions leave a comment on this blog or send me a tweet on twitter to @dhirshjr. And as always the code from this post is up on Github at the following URL.

https://github.com/dhirshjr/pricebook

Meteor JS (Part 6): Authentication & Authorization

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

If you would like to follow along I have tagged the git repository on Github at the starting point for this post. Here is the URL to the tag

https://github.com/dhirshjr/pricebook/releases/tag/v0.5.0

By default the insecure and autopublish packages are part of every meteor project. Autopublish publishes all records from every collection. Insecure allows full CRUD operations against all collections. In this post we are going to remove autopublish and insecure. We will also enable authentication and authorization.

So let’s get started.

Remove Autopublish

The reason we need to remove autopublish is because it publishes all collections to the client. We don’t want this because once we have a users collection then all details of all of our users will be published. This includes all email addresses, encrypted passwords, oauth refresh tokens, etc.

First we will remove autopublish by running the following.

If you have the application running you will notice that the data is gone. We are no longer publishing everything. We will need to create a publication on the server and a subscription on the client.

Let’s add the following code to server/publications/items/item.js. The path to this file doesn’t exist yet. I had to run mkdir -p server/publications/items from the terminal before creating the file.

The Meteor.publish method is taking in two arguments. First, we give the publication the name of allItems. Next, we pass in a function that returns a collection cursor. This completes us getting set up on the server side.

Next we will add a the following to the top of client/views/items/itemlist.js.

Remove Insecure

At this point we only have access to data that the server has published. The only problem is that we can run insert, update, and remove against anything that has been published. Next we are going to remove the insecure package. Once we remove insecure we will not be able to make any changes to the published data.

Run the following, at the terminal, to remove insecure from our project. After running this try to modify data within the application.

To restore our ability to modify the data we are going to need to do a few things.

First we will need to call the allow function on our Items collection. This function takes in an object that specifies the rules that allow someone to modify data. Let’s add the following code to both/collections/items.js just below the creation of the Items collection.

As you can see we are passing in a function for each modification operation. If you look closely you will notice that these functions are taking in a user ID. At this point we are not authenticating so it will be null and we still won’t be able to modify anything.

Authentication and Authorization

It is pretty easy to add an accounts system to our project. Since we are using Semantic UI I thought that it would be important to find a system that supported our front end UI library. For this project are going to use useraccounts package. Just run the following to install.

That last package might leave you scratching your head a little. Useraccounts:core allows us to plugin whatever accounts authentication package we want. There are several others. For example take a look at accounts-google or accounts-github.

The useraccounts:core package is going to change quite a bit. The biggest change is that it is going to add iron:router to our project. Iron router will allow us to do a lot in the future so let’s go ahead and make the changes needed for migrate to using Iron Router.

First, let’s create our routes. Let’s create the file both/routes.js and put the following code into it.

First, we are telling the useraccounts that we want to configure the signIn route. This will allow us to sign in by navigating to /sign-in and get a login/registration page. Next, We are configuring our layoutTemplate to point to a template named layout. We will create the layout template next. Finally, we are configuring a route for the root of our application and this route will render the itemList template.

Next, let’s create the layout template. Create a file at client/views/layouts/layout.html and add the following code.

You should notice something new in this template. Instead of hard coding the {{> itemList}} inclusion tag, we are now using a {{> yield}}. This is simply saying this is where we put the template specified by the render method in our route definition.

Now, at this point we need to go back to pricebook.html and clear out the body tag. Your body tag in the pricebook.html file should look like the following now.

At this point you should be able to register accounts and sign in. But you still won’t be able to add or modify anything. The reason is that our allow rules are looking for an owner property and it simply doesn’t exist yet. The owner property will simply be the unique Mongo ID of the user that is logged in. To get the userId into the owner property let’s add the collection hooks package by running the following.

This package installs several hooks that we can tap into. The hook we are going to use is before.insert.

Let’s add the following code to the both/collections/items.js file just after the Items collection definition.

The code in the callback will run on the client and server. I encourage you to take a look at all of the documentation for the collection hooks package on Github.
https://github.com/matb33/meteor-collection-hooks

We also need to update our simple schema for the Items collection. If we don’t then we still won’t be able to make any changes. In the same file add the following code to add owner to the schema.

At this point you might want to reset all of the data in the project. The owner property won’t exist on any preexisting data. To reset the project let’s run the following from the terminal. If meteor is running let’s stop it before running the reset.

Now let’s try creating some users and adding some data. This should work at this point. One thing to note is that a user will only be able to modify records that they own. In the next section we will modify the UI to show this.

UI

Now a user can insert items and modify items that they own. But it might be a little confusing to leave things how they are. A user might not understand why a change was rejected. A better way of handling this is to have the UI let the user know they cannot add or edit.

First, let’s create a UI helper that we will be able to use in any of our templates. Let’s create client/lib/helpers.js to store our global template helpers. Let’s add the following code to helpers.js.

We are putting the helpers.js file into the lib directory, because meteor will load all files in the lib directory before any other files under the client directory.

Then, let’s modify the addItem template to only show the form when a user is logged in. Modify client/views/items/addItem.html to look like the following.

This will use our new isLoggedIn global helper.

As a side note, you might have to sign out and back in again to get the add items button to work.

We only need to modify a few more files.

In client/views/items/itemlist.html let’s hide the last column of the header if a user isn’t logged in.

In client/views/items/item.html let’s hide the last column of an item row if a user isn’t logged in.

This will only show the edit and delete buttons if we are logged in and own the item. Notice that once you save this file even if you are logged in you cannot see the edit and delete buttons. That is because we used a new template helper called canEdit. We need to add that to the client/views/items/item.js file.

Summary

In this post we removed packages that made our application insecure. We explicitly stated what data we wanted to publish to the client. We added authorization rules and authentication. If you have any questions leave a comment on this blog or send me a tweet on twitter to @dhirshjr. And as always the code from this post is up on Github at the following URL.

https://github.com/dhirshjr/pricebook

Running Meteor JS with Phusion Passenger and Nginx

These are the steps I used to get a Meteor JS application deployed to my development server. I am assuming that the box you are running on is already secured. These instructions have been tested with Ubuntu 14.04 server. I executed the instructions at the terminal, as root. Some of the instructions will need to be done as www-data. Depending on the state of your system you might need to run an apt-get update and install curl before you get started.

  1. Create the directory where you will store sites and setup the permissions for the www-data user.

  2. Install Meteor JS and Phusion Passenger
    1. curl https://install.meteor.com/ | sh
    2. Install key from phusion’s repo.
    3. Create /etc/apt/sources.list.d/passenger.list and add the repository by running the following.
      1. echo “deb https://oss-binaries.phusionpassenger.com/apt/passenger trusty main” >  /etc/apt/sources.list.d/passenger.list
    4. chmod 600 /etc/apt/sources.list.d/passenger.list
      1. Secure this file.
    5. apt-get install apt-transport-https ca-certificates.
    6. apt-get update
    7. apt-get install nginx-extras passenger
      1. Note: Always install nginx-extras because it comes with the phusion module compiled into the server.
  3. Configure Nginx
    1. Edit /etc/nginx/nginx.conf and uncomment the following lines.
      1. passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.in;
      2. passenger_ruby /usr/bin/ruby;
    2. The nginx.conf file includes all conf files in the /etc/nginx/conf.d/ folder. So let’s create a leaderboard.conf file in that folder. Paste the following into the new file.

  4. Create the Example Meteor Application – Note: These instructions must be run as www-data.

    1. ctrl-c to stop meteor
    2. Create the directories needed by Passenger. You pointed to the public folder in the leaderboard.conf file.
      1. mkdir tmp public
    3. Type exit to get out of your session as www-data.
  5. Finish
    1. service nginx restart
    2. open browser and navigate to your server.
    3. Enjoy!
  6. Troubleshooting
    1. Nginx logs to /var/log/nginx. When trying to get this to work I would tail the error log by running the following.
      1. tail -f /var/log/nginx/error.log

Meteor JS (Part 5): Styling with Semantic UI

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

Up until now the application has looked pretty drab. In today’s post we are going to fix that using Semantic UI. Semantic UI is an open source, HTML UI, framework. It has a very expressive syntax for expressing UI elements.

Here is the link to their home page and documentation.

http://semantic-ui.com/

First thing we will need to do is add the Semantic UI meteor package to our project.

/pricebook.html

Let’s setup the header menu and put the item list inside a Semantic UI grid. Also let’s add the viewport meta tag to allow the page to scale properly on a mobile device.

/pricebook.css

Create pricebook.css and add the following rule to give the body tag a 5px margin.

/client/views/items/itemlist.html

First, let’s move the add item template outside of the table. Later we will convert the add item template into a collapsible panel. Next, let’s add a message parameter to our itemerrors template. We will use this a little later. Finally, we are going to set each column to be two columns wide using the Semantic UI grid.

/client/views/items/itemerrors.html

Let’s convert our simple errors template into a Semantic UI error message. We want our error to look something like this.

StyleErrors

/client/views/items/item.html

Let’s style the editing template. Now we are using Semantic UI form and input elements. Also we will style the buttons into Semantic UI button elements.

/client/views/items/item.js

Now let’s clear out the validation context when we click cancel or edit within the item template.

/client/views/items/additem.html

Let’s move the add item form into a collapsible accordion panel and style our inputs to make them look a little better.

/client/views/items/additem.js

Let’s move the insert and reset code into their own functions. Next we will catch the events for submit, save, and cancel. After that let’s initialize the accordion and set focus on the first input. Last we will catch when escape is pressed and reset the form.

I want to draw attention to the rendered function. When this function is called, this is set to the template instance. The issue is that when the open event fires on the accordion this will not be the instance of the template. So here we set self equal to this. Self will become our template instance and we will be able to access it within the onOpen callback.

Summary

In this post we styled the Price Book application. The application isn’t finished but it looks a lot better.

Here is a before and after screen shot of the UI.

MeteorJSGettingStartedPart4ItemNormal
Before
StyledApp
After

Over the next post or two we will discuss data security. Also we will talk about authentication and authorization.

I hope you continue to check out this series. If any of you have suggestions please let me know. You can leave a comment here or send a tweet to @dhirshjr on twitter. As always I have put the code from this post up on github at the following link.

https://github.com/dhirshjr/pricebook

Meteor JS (Part 4): Editing & Input Validation

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

Today we are going to talk about editing items in our price book and about validating the data that is being entered. The UI is still very rough but we will get to it in another post.

In Line Editing

This feature will allow the user to edit an item that has been added. We will add an edit button. When the user clicks the edit button the data will be replaced with inputs. The user can either click save to save the changes or cancel as shown below.

MeteorJSGettingStartedPart4ItemNormal
Normal
MeteorJSGettingStartedPart4ItemEditing
Editing

To make this possible we will modify the item template. Then we will add the events and helpers needed to make everything work.

First modify /client/views/items/item.html by replacing it with the following markup.

The lines of code that need to be added have been highlighted. The first thing you should notice is the {{#if editing}} block. We will be creating the editing helper in our JavaScript. When editing is true the editing portion of the item template will be shown. The item’s values will filled in each input.

Adding the following code to /client/views/items/item.js will add the editing helper used in the if block. Also let’s go ahead and add the event for the edit item button.

This brings us to a new concept. Session is a client side, global, reactive dictionary that survives hot code pushes. Session has three methods: get, set, and equals. This means that whenever the Session variable of editItemId changes we will fire off the editing helper function. Our template has a dependency on the editing helper and therefore will update itself. This is what it means to be reactive. We don’t have to do things manually to the DOM to change between a normal and editing states.

Also we are going to add the click event for the edit button. The only thing we need to do is set the session editItemId variable to the current ID. This is all we need to do to get the edit form to show up.

Next, let’s make the Cancel button work by adding the following code to our item.js file.

All we need to do is set the session variable editItemId to null. As soon as this happens the input fields will go away.

Next let’s wire up the save button. First let’s create a method to save the item.

All this function is doing is pulling the values out of the input controls. Then we run an update on the item. After that is done we switch back to a normal view by setting our session variable to null.

Now let’s add the event handler for the save button.

Finally, we are going to wire up a couple of keyboard events. When a user presses enter let’s save the changes and when a user presses escape let’s cancel the changes.

That is pretty much it for editing. Next we will visit input validation.

Input Validation

Right now we are not validating anything. Go ahead and try sending back a word for price. I promise it will work. So we need to add input validation. For this project I have chosen to use a package called collection2. The nice thing about collection2 is that it adds the validation rules to the insert on the client and server. You only have to write the validation rules once.

To install the collection2 package just run the following in a terminal within your project’s directory.

Documentation for collection2 can be found on the project’s github page.

https://github.com/aldeed/meteor-collection2

Next we will create SimpleSchema object with the validation rules and then we will attach it to the collection. Add the following code to the /both/collections/items.js

Now we have a schema with our validation rules. Next we need to update our insert and update calls so we will be able to intercept validation errors. Let’s modify our /client/views/items/additem.js file to look like the following.

All we have done is updated the insert method. The new signature takes an object that let’s us name our validation context and a callback to handle the response. As you can see if we don’t find an error we just run the code that was already there.

We need to do the same to the update call, within the saveItem function, in /client/views/items/item.js.

Now let’s create a template that will list our validation errors. This is incredibly easy. Once we have created the template all we will need to do in our additem and itemlist template is add the following.

{{> itemerrors contextName=’name of our context’ }}

Here is the HTML file. /client/views/items/itemerrors.html

There are a couple of things going on here. First, we are looping a helper called errors. Next, we are creating a div with the message text within it. Last, we are setting the class attribute of the div using the contextName parameter that a was passed in. You might be wondering what is up with the ../contextName syntax? An #each block creates a new data context based on the item in the loop. To access parameter contextName we have to use the ../ syntax to access the parent’s data context.

Here is the JS file. /client/views/items/itemerrors.js

We create an errors helper that is going to return a reactive list of error messages. After that, we create the attributes helper that will set the class to include the context name. This will allow us to style all errors or we can be more specific based on the context name.

Next we need to call our errors template within the itemlist and additem templates.

/client/views/items/additem.html

/client/views/items/itemlist.html

Summary

At this point we should have the ability to edit items and validate our insert and update operations. Over the next couple of posts we will visit data security and ui styling. And as always the code from this post is up on Github.
https://github.com/dhirshjr/pricebook

Thanks for stopping by.

Meteor JS (Part 3.5): Directory Structure

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

This is going to be a short post. That is the reason for calling this part 3.5. I wanted to write a post discussing the directory structure of the price book application.

Old Directory Structure

Old Directory Structure

New Directory Structure

New Directory Structure

Meteor JS allows for a very flexible project layout.

All I have done here is move each template element into its own html file and moved template specific JavaScript into its own js file. I moved the Items collection into the both folder. The both folder is a personal preference. It reminds me that the code in that folder is available to the client and server environments. Now all the pricebook.html file contains is just the head and the body elements.

You can put your files anywhere in the directory tree, but there are a couple of special folders that I would like to call out.

Client

Items that we put in the client directory, or in a sub directory of the client directory, are sent down to the client, and can only be accessed from JavaScript on the client.

Server

Items that we put in the server directory, or in a sub directory of the server directory, are only available on the server. This code will never be seen by the client. This is a good place to put code that might contain sensitive information like a password or client secret.

Any Other Folder

Contents put in any other folder will be available to the client and the server.

Summary

I have linked to the Meteor JS documentation that goes into this subject with a lot more detail.

http://docs.meteor.com/#/full/structuringyourapp

I have checked in this change to the Github repository. I would recommend cloning the repository but if you would like to make the changes by hand it shouldn’t be too difficult.

https://github.com/dhirshjr/pricebook

Meteor JS (Part 3): Inserting and Deleting Data

This is a post in a multiple part series on getting started with Meteor JS. If you don’t have Meteor JS installed you will want to start at  the first post in the series. Part 2 is where we start building the application.

Welcome back! Today we are going to add a few more fields to our price book item object, create an add item form, and add the ability to delete.

First, we will start with the HTML and then we will work on the JavaScript.

HTML (pricebook.html)

Item List Template

The first thing we want to do is replace the itemList template with the following HTML. This will get the main list ready for the additional fields needed to record pricing data. We are still looping through the results of the items helper and adding an inclusion tag for the addItem form. If Meteor is running and you try to save this file now you will get an error. The addItem template doesn’t exist yet.

Item Template

Next, we will replace the item template with the following. All we are doing here is telling Meteor that we want to display the new properties that are going to be available and adding a new delete button. The interesting thing is that the delete button is going to be very easy to wire up. More on that in the JavaScript section of this post.

Add Item Template

Finally, we are going to add this to the bottom of our pricebook.html file. This is just a simple form that will allow us to add new items. At this point let’s save our pricebook.html file. It is time to move on to the JavaScript.

JavaScript (pricebook.js)

Template Events

Each template has an events function that can take an event map. The event map format is very simple.

The nice thing about the selector is that it is scoped to the template.

For more information check out the Meteor documentation.

Meteor Documentation on Event Maps

Add Item Template Events

Let’s add the following code to the if(Meteor.isClient) block. All we are doing here is creating an object from the form fields, inserting the object, and clearing out the form. The return false is there to prevent the form from being submitted to the server. As you can see the only line of code that is needed to insert

Item Events

Let’s add the following code to the if(Meteor.isClient) block. This code is even simpler. The cool thing is that this is assigned to your data context for the template. The item template was created with an {{#each}} block. This means that each item in our list knows about itself.

Summary

Today we made the application a little more usable. I know it is still far from usable. Also if you where thinking to yourself, “man this app looks terrible”, you would right. I am intentionally leaving the styling for a future post.

In the next post we will add edit functionality and input validation. Thanks again for stopping by. If you have any questions leave a comment or send a tweet to @dhirshjr. As always the code from this post is available on Github.

https://github.com/dhirshjr/pricebook/