Aschenblog: Thoughts on Code and Fabrication

Workflow: Ruby on Windows With Vagrant

I wanted to share a workflow that I use to do Ruby development on Windows using Vagrant. I should mention that I always prefer to do Ruby development on a Mac or running Linux, but this workflow might work well for someone doing both .NET and Ruby development.

This general approach should work in most cases where the development environment does not match the deployment platform. As a first step, I suggest trying out the Ruby Installer for Windows. I’ve found that this works for small projects, but have trouble with larger projects especially with a large number of gem dependencies.

The workflow involves using your native environment to launch a virtual machine that more closely matches the one use to deploy your code in production. You get the benefits of the windowing system tools (Sublime Text, RubyMine, Notepad++, etc) with the benefits of running specs and code in an environment closer to production.

The Value of Professional Development

One of my career goals is to keep getting better at what I do. When I was a manager, I always tried to help my employees be their best by coaching and by offering them training opportunities. For example, I offered my sales staff classes in computer aided design because it helped them to communicate between our customers and fabrication shop. In turn, this created more opportunities to add value to our clients.

As an employee, I take the initiative. I fight stagnation by reading books and blogs, watching videos and most importantly- I practice my craft. I think to be great you need to read a lot of code and write a lot of code. When I do work-related side projects outside of work, I share it with my boss and co-workers. This helps to demonstrate my passion and capabilities to the team.

I ask for more responsibility and work that challenges me. I push myself. I inquire if I can go to conferences. They are a great opportunity for me and my team to keep abreast of new technologies. Learning together builds morale and allows us to make connections by networking with members of our community. I attend technical meet ups each month in the Portland area and find it to be stimulating and good time. If you are in the area you should stop by the Portland Ruby Brigade meet up. Free beer and pizzas on the first Tuesday of the month.

Overall, I try to take my career in my own hands. It’s my responsibility to make sure I continue to develop my skills to be successful in my career both today and in the future.

Let It Snow With the Accuweather Gem

AccuWeather is one of the standard widgets on my Android phone. I found no API wrapper for it after searching rubygems.org and I thought it would be fun to write one after spending a little time reverse engineering the web API (related post).

First, find a location via the city search API:

1
2
3
require 'accuweather'

locations = Accuweather.city_search(name: 'vancouver')

This returns a list of locations that match the input. Each location provides several pieces of information including a city identifier:

1
2
3
4
5
6
7
vancouver = locations.first

vancouver.id        # => 'cityId:53286'
vancouver.city      # => 'Vancouver'
vancouver.state     # => 'Canada (British Columbia)'
vancouver.latitude  # => '49.2448'
vancouver.longitude # => '123.1154'

The gem caches over 175K cities around the world. If the location name is not in the cache, the gem will make a HTTP request to AccuWeather to try and find it.

The location identifier is used to get more information about current weather conditions:

1
2
3
4
5
6
current_weather = Accuweather.get_conditions(location_id: 'cityId:53286').current
current_weather.temperature    # => '41'
current_weather.weather_text   # => 'Partly Sunny'
current_weather.pressure       # => '30.35'
current_weather.humidity       # => '43%'
current_weather.cloud_cover    # => '40%'

A week of day and nighttime forecasts are available:

1
2
3
4
5
6
7
8
9
10
11
weather_forecast = Accuweather.get_conditions(location_id: 'cityId:53286').forecast
last_forecast_day = weather_forecast.last
last_forecast_day.date        # => "12/3/2015"
last_forecast_day.day_of_week # => "Thursday"
last_forecast_day.sunrise     # => "7:49 AM"
last_forecast_day.sunset      # => "4:16 PM"

# Get the dates, daytime high and nighttime low temperatures
weather_forecast.map(&:date)                             #  => ["11/27/2015", "11/28/2015", "11/29/2015", "11/30/2015", "12/1/2015", "12/2/2015", "12/3/2015"]
weather_forecast.map(&:daytime).map(&:high_temperature)  # => ["45", "45", "47", "44", "44", "48", "48"]
weather_forecast.map(&:nighttime).map(&:low_temperature) # => ["27", "28", "31", "32", "40", "42", "36"]

The API returns a weather_icon number for the current weather and forecast. This number appears to correspond to PNG file from AccuWeather.com. I downloaded the full set of PNG files and included them here, but found a handful of better looking weather webfonts out there (links below).

Related resources:

Accuweather gem links:

Stay warm out there and have fun!

Open Source Pirate Swords

Every year my work has a competition for who can come up with the best Halloween decorations. My team has the good fortune of being in a large conference room, which gave us a distinct advantage over the pod layout elsewhere in the building. Our team’s resident creative genius Sheena came up with the concept of dead pirates / death in the deep. As a companion to our pirate costumes, I created a basic pirate sword design with a curved blade:

I cut the swords out of a sheet of ½” cabinet grade plywood. This is important because it has a flat and smooth pre-finished surface, which I found very easy to prime and spray paint. I used silver metallic paint for the blade portion and then used a metallic bronze for the hilt. Painters tape was used as masking to provide a sharp line between the two colors.

As a final touch, I used a hot glue gun to attach 1/8” leather cord to the handle of the hilt.

Here are the sword designs in various formats, if you want to make your own:

Reverse Engineering Web APIs Using Mobile Apps and a Packet Sniffer

Recently I have been having fun using a packet sniffer running on my Android phone to explore non-public APIs. I will provide a small example using the overstock.com app to look into current Flash Deals. This technique can be used to monitor a large variety of network traffic running on the phone. The app I used also decrypts SSL packets, which is useful for viewing HTTPS traffic.

First, I installed Packet Capture for Android. The application requires permission to monitor network traffic when you press the Play button. Please do so at your own risk. Also, this captures a large amount of data, so you might want to limit the length of time it is running.

I downloaded the Overstock.com Android application, started it and then started the packet capture. I tapped FLASH DEALS and then stopped the packet capture. The last image (above right) shows the HTTP request and response that corresponded to my interaction with the application. You may have to look through a large number of packets to identify the one of interest.

While the response is gzipped, I’m more interested in the HTTP request. The first thing I tried was a simple curl from the command line to reproduce the application:

$ curl http://www.overstock.com/api2/flashdeals/current?offset=0&limit=12

In some cases, you may need to use the same HTTP request headers (use the -H "<HEADER_KEY>: <HEADER_VALUE>" command line option) to convince the remote HTTP server that the request was made from their application. However, this was not needed for the Overstock.com web API. Here is a snippet of the response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
{
   "nextHref":"",
   "currentTime":"2015-10-14T23:37:29.399-06:00",
   "items":[
      {
         "id":30905,
         "offerType":"FIXED_PRICE",
         "startDateTime":"2015-10-14T10:00:00.000-06:00",
         "endDateTime":"2015-10-15T10:00:00.000-06:00",
         "storeId":43,
         "departmentId":454,
         "categoryId":4540,
         "subCategoryId":1399,
         "priority":1,
         "skus":[
            {
               "fixedPrice":124.99,
               "product":{
                  "productHref":{
                     "id":10181037,
                     "href":"http://www.overstock.com/api/product.json?prod_id=10181037"
                  },
                  "productName":"Jennifer Taylor Collection Red Moraccan Cotton 3-piece Duvet Cover Set",
                  "images":{
                     "thumbnail":"http://ak1.ostkcdn.com/images/products/10181037/T17307668.jpg",
                     "medium":"http://ak1.ostkcdn.com/images/products/10181037/P17307668.jpg",
                     "large":"http://ak1.ostkcdn.com/images/products/10181037/Moracan-Collection-3-piece-Cotton-Duvet-Cover-Set-fd47bab9-10f3-49e2-a431-5ae4cf999abf_1000.jpg"
                  },
                  "pricing":{
                     "compareType":"MSRP",
                     "sellingPrice":124.99,
                     "comparePrice":352.00,
                     "onSale":true,
                     "discount":227.01,
                     "savingsPercent":64,
                     "maxPrice":124.99,
                     "minPrice":124.99
                  },
                  "reviewInfo":{
                     "count":0
                  }
               }
            }
         ]
      },
      ...

There was a short description, pricing information, image links in three sizes hosted on the CDN and a secondary link:

$ curl http://www.overstock.com/api/product.json?prod_id=10181037

Here is the full response:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
{
   "id":10181037,
   "name":"Jennifer Taylor Collection Red Moraccan Cotton 3-piece Duvet Cover Set",
   "meta":{
      "apiUrl":"http://www.overstock.com/api/product.json?prod_id=10181037",
      "htmlUrl":"http://www.overstock.com/Bedding-Bath/Jennifer-Taylor-Collection-Red-Moraccan-Cotton-3-piece-Duvet-Cover-Set/10181037/product.html",
      "imageBaseUrl":"http://ak1.ostkcdn.com/images/products/"
   },
   "sku":"17307668",
   "imageThumbnail":"10181037/T17307668.jpg",
   "imageLarge":"10181037/L17307668.jpg",
   "imageMedium1":"10181037/P17307668.jpg",
   "priceSet":[
      {
         "label":"MSRP:",
         "formattedPrice":"$352.00",
         "priceType":"COMPARISON_PRICE"
      },
      {
         "label":"",
         "formattedPrice":"$227.01(64%) off",
         "priceType":"DISCOUNTED_AMOUNT"
      },
      {
         "label":"Sale:",
         "formattedPrice":"$124.99",
         "priceType":"CURRENT_PRICE"
      }
   ],
   "messages":[
      {
         "message":"Earn $2.50 (2%) Rewards on this product FREE with Club O Silver",
         "messageType":"JOIN_CLUBO_SILVER"
      }
   ],
   "oViewerImages":[
      {
         "id":"fd47bab9-10f3-49e2-a431-5ae4cf999abf",
         "cdnPath":"10181037/Moracan-Collection-3-piece-Cotton-Duvet-Cover-Set-fd47bab9-10f3-49e2-a431-5ae4cf999abf.jpg",
         "originalWidth":1024,
         "originalHeight":1024,
         "imageSizes":[
            {
               "imagePath":"10181037/Moracan-Collection-3-piece-Cotton-Duvet-Cover-Set-fd47bab9-10f3-49e2-a431-5ae4cf999abf_320.jpg",
               "height":320,
               "width":320
            },
            {
               "imagePath":"10181037/Moracan-Collection-3-piece-Cotton-Duvet-Cover-Set-fd47bab9-10f3-49e2-a431-5ae4cf999abf_600.jpg",
               "height":600,
               "width":600
            },
            {
               "imagePath":"10181037/Moracan-Collection-3-piece-Cotton-Duvet-Cover-Set-fd47bab9-10f3-49e2-a431-5ae4cf999abf_1000.jpg",
               "height":1000,
               "width":1000
            }
         ]
      }
   ],
   "memberPrice":124.99,
   "compareAt":250.27,
   "categoryId":4540,
   "subCategoryId":1399,
   "altSubCategoryId":22240,
   "shortDescription":"This Moracan Collection duvet set is woven of 100 percent cotton for softness and comfort. Rich blues and oranges in a geometric pattern complete the look. ",
   "description":"This reversible duvet cover features a blue and orange geometric design on one side and a solid blue on the other. Crafted of quality cotton, this set is conveniently machine washable.<br><br><ul><li>Includes: 3-piece Set</li><li>Reversible Bedding</li><li>Material: Cotton</li><li>Pattern: Graphic</li><li>Color: Red, Orange, Blue</li><br><br><b>Queen-size dimensions:</b><li>Duvet: 93 inches x 96 inches</li><li>Standard sham: 20 inches x 27 inches</li></ul><br><i>The digital images we display have the most accurate color possible. However, due to differences in computer monitors, we cannot be responsible for variations in color between the actual product and your screen.</i><br>",
   "savingsPercentage":50,
   "basePrice":124.99,
   "hideMAPProductPrice":false,
   "mediaItem":false,
   "mediaReleaseDate":"",
   "clubODiscount":false,
   "nonClubODiscount":true,
   "preRelease":false,
   "priceLabel":"Sale",
   "online":true,
   "options":[
      {
         "optionId":15463578,
         "decription":"N/A",
         "qtyOnHand":31,
         "maxQuantityAllowed":20,
         "price":124.99,
         "comparePrice":352.00,
         "compareType":"MSRP",
         "hideMAPProductPrice":false,
         "savings":227.01,
         "savingPercentage":64,
         "onSale":true,
         "showAmazonPrice":false,
         "priceSet":[
            {
               "label":"MSRP:",
               "formattedPrice":"$352.00",
               "priceType":"COMPARISON_PRICE"
            },
            {
               "label":"",
               "formattedPrice":"$227.01(64%) off",
               "priceType":"DISCOUNTED_AMOUNT"
            },
            {
               "label":"Sale:",
               "formattedPrice":"$124.99",
               "priceType":"CURRENT_PRICE"
            }
         ]
      }
   ],
   "warranties":[

   ],
   "flashDeal":{
      "id":30905,
      "href":"http://www.overstock.com/api2/flashdeals/30905",
      "currentTime":"2015-10-14T23:43:20.413-06:00",
      "startDateTime":"2015-10-14T10:00:00.000-06:00",
      "endDateTime":"2015-10-15T10:00:00.000-06:00"
   },
   "shipDeliveryEstimates":{
      "optionId":15463578,
      "productType":"REGULAR"
   },
   "taxonomy":{
      "store":{
         "id":43,
         "name":"Bedding & Bath",
         "apiUrl":"http://www.overstock.com/api/search.json?taxonomy=sto43",
         "htmlUrl":"http://www.overstock.com/Bedding-Bath/43/store.html"
      },
      "department":{
         "id":454,
         "name":"Fashion Bedding",
         "apiUrl":"http://www.overstock.com/api/search.json?taxonomy=dep454",
         "htmlUrl":"http://www.overstock.com/Bedding-Bath/Fashion-Bedding/454/dept.html"
      },
      "category":{
         "id":4540,
         "name":"Duvet Covers",
         "apiUrl":"http://www.overstock.com/api/search.json?taxonomy=cat4540",
         "htmlUrl":"http://www.overstock.com/Bedding-Bath/Duvet-Covers/4540/cat.html"
      },
      "subCategory":{
         "id":1399,
         "name":"Duvet Covers",
         "apiUrl":"http://www.overstock.com/api/search.json?taxonomy=sto1399",
         "htmlUrl":"http://www.overstock.com/Bedding-Bath/Duvet-Covers/4540/cat.html"
      }
   },
   "originalOptionCount":1
}

Renumber Filenames Sequentially

renumber is a small command line utility that renames files in a single directory. Files are sorted in lexicographic order and then renamed with sequential numbers. It is possible to specify a prefix and suffix - these are optional parameters. There are a couple of examples of usage below.

The code is open source and available on GitHub.

Command Line Usage

$ renumber <directory> <prefix> <suffix>

Arkanoid Game Levels

I loved playing Arkanoid when I was a kid. I spent hours playing the game over at my friend’s house when I was growing up on the original Nintendo Entertainment System. It was rated as one of the top games after its release in 1986.

I found a great Arkanoid background map set on NES maps by Rick Bruns (see below). Each level was perfectly aligned at 192 pixels wide by 232 pixels tall. The top, left and right edges were 8 pixels. Each brick was 16 pixels wide by 8 pixels tall. With a little math (and confirming in Photoshop), I found that the background could support exactly 11 brick tiles wide by 28 tall.

This made the map set the perfect asset for image parsing. My plan was to take the image as input and generate source code with level data as output. Finally, I used a 3D graphics library to render the levels in browser.

Image Parsing

I wanted to use the Chunky PNG and Oily PNG gems for image parsing. The former is a 100% Ruby implementation for encoding and decoding PNG images with read / write access at the pixel level. The latter gem uses native C extensions to improve encoding and decoding speed for Chunky. I used these gems on other projects with good success.

Building a Custom Machined Raised Bed

Looking forward to sunshine and warmer temperatures, I decided to take on a project over the weekend to build a small raised bed so we could try planting some peppers and herbs this spring. My wife asked that the bed be around 18” tall. I went to work in my project notebook and came up with the following hand sketch:

I wanted to use 4” x 4” posts for the corners and 2” x 6” boards for the walls of the planter box. There were a couple of design elements that I wanted to incorporate. First, I wanted the 2” x 6” boards to be recessed into the post. Also, I wanted to put a 45 degree chamfer on the top of each 4” x 4” for decoration. I decided that the overall dimensions should be around 36” x 60” so I could maximize utilization of the 8 foot lengths of the 2” x 6” stock board size.

Materials