Use the JIRA API to Post Tickets to JIRA

A while ago, I built this super basic Sinatra app to post tickets to JIRA. Here’s the use case: you have non-technical people who are part of your company/team that need to be able to add bugs to JIRA. However, they aren’t putting the right information into the ticket. Here comes this super basic app. To get it running, you just need to update .env with your JIRA username, password, and project key. However, I would recommend changing it to use OAuth. Right now, the form is very simple and, if you decide to use this, I would highly recommend you update it to ask for whatever information you want. Just don’t forget to update the JSON in sinatra_jira.rb! This application is completely open source - feel free to copy any of it for any reason, whole or partial. Let’s dig in a bit and do a quick overview of how Sinatra works.

To start off, the Gemfile is minimal. The biggest thing is that I’m using dotenv, a super useful gem that helps manage environment variables using .env files. Other than that, rubocop, sinatra, and we are using thin for the server.

The main file (sinatra-jira.rb) contains the routes and the actions. It’s basically a combination of a controller and routes file all in one. The initial get just displays the form and all the work happens in post. Even that is fairly simple though… we just take the field contents and put them in the form that the JIRA API wants.

The form is pretty simple too and really ugly. I would definitely recommend adding some styling and don’t be like me… internal users deserve nice looking apps too! Since the problem I was facing was that I wasn’t getting the right information, I made sure to put examples in the form to increase the chance that I would get the information that I need.

This is a SUPER basic response. Don’t miss that we are passing key to the response. That is the issue key which, depending on how much your end users use JIRA, might be useful to include.

Hope this was somewhat useful in some way. I’d love to see feedback too!

Cookie Authentication using Scalatra and JWTs

As part of my work with Arcadia, I've built a Rails application that added a cookie that contains a JWT (pronounced jot). Great! That was fairly simple. Then I had to go over to our Scala application and get it to accept the JWT as identification. Right now, we were keeping it pretty simple and we only care if it's valid. This post will cover what I think is the simplest way to do that, from start to finish. Or you can skip all that and just go look at the full gist.

We want to start off with the JWT parsing. And before we add the code to actually do that, let's add some tests! I decided to use the JWT Scala library and, in particular, jwt-core. It had, in my opinion, the most easy-to-understand documentation so I could indeed RTFM and get my work done. Since I didn't need to add encoding in the actual application (the tokens would be encoded in another application), I added a quick line to encode a token within the tests.

Now that I have my tests, let's add the actual code to decode the JWT! Thanks to JWT Scala, this is pretty simple! The real secret sauce is in this line: userTokenData = parse(decoded).extract[Token].data. That does a lot of heavy lifting! decoded is just a string and parse turns it into this Jvalue object thanks to json4s, but that object is a bit hard to work with. However, I can extract it out to my case class, Token, which is downright magical. If it doesn't include all the fields that I have in Token, it will produce an error. Perfect!

Next I need a reusable Authentication object. This wasn't too bad because I found out that HttpServletRequest has a method called getCookies which... returns the cookies. Excellent. I'm sure this looks weird as an Either, but in this case I really did want Some or None because I didn't care about returning the error to the actual user. I did want to log it though, hence the liberal use of println.

Last, but definitely not least, I need a servlet. Well... tests for the servlet, then the servlet 😛. This is where I actually ran into trouble because I wasn't sure how to pass cookies to the get request in a test. With some help from my boss, we found out that get takes a headers param and you can pass a cookie if it looks like this: headers = Map("Cookie" -> cookie_value). To be honest, it required a bit of trial and error and I'm still trying to figure out exactly what values are being passed.

Screen Shot 2018-05-02 at 11.33.45 AM.png

And finally... my servlet! Short and sweet.

RailsConf 2018 Recap

I went to RailsConf last week and it was an amazing experience. DHH's keynote reminded me why I love Rails. Eileen's keynote made me super pumped for Rails 6. And all the talks were a delightful reminder of why I love programming and why I love the Ruby community. Here are some deeper thoughts and notes, divided up by talk:

Note: I'll post links to talks that I reference as soon as they are up.

DHH's keynote

This talk really hit home for me since I was very recently battling with Play for 4 months. One of the things that I do really love about Rails is that I can focus on solving the problem I actually want to solve, not problems that have been solved before (like... authentication). And, while I do agree that junior developers and people just starting out should not have to know SQL, I do think that knowledge of SQL is still useful if you want to be a good Rails developer. Relying solely on ActiveRecord is a mistake.

Crash Course in RSpec: stubs and doubles and mocks -- oh my!

This workshop managed to be both good and not quite what I wanted. I had hoped by the title that there would be a big emphasis on stubbing, but it was more of a footnote. It was a good crash course though and if you don't have much prior RSpec experience, check out Nicole's tutorial.

Interviewer Skills

Jennifer Tu of Cohere gave an excellent workshop on interviewer skills that I have about 4 pages of notes from that I will try to sum up here. One of the first things she brought up was that a team should have specific goals in mind when interviewing:

  • What values does the team have?
  • What characteristics does the candidate have?
  • What actions does the candidate take in certain situations?
  • What makes someone successful on my team?

For each attribute that the interviewers want the candidate have, they should ask questions that dig into how a candidate behaves. For example, if your team values kind feedback, instead of asking "Do you give kind feedback?" or "Are you nice when responding to pull requests?", ask "Have you ever given feedback to someone whose code was not good? What did you do? Why?". If you value independent learning, ask:

  • How do you learn something new?
  • Do you have an example of a time when you ran into code you didn't understand?
  • Share a time when you had a problem dumped into your lap but you had no idea what to do.

Make sure to wrap a question in context to ensure the candidate fully understands what you are asking. For example, one of the attendees wanted independent thinkers and people who would question decisions. They were currently asking this question:

You get a user request to add a blue button. How do you add a blue button?

However, in the context of an interview, someone who would normally question a decision like that might resonable think that the interviewer just wants to know if they know how to add a button to a page in HTML. What they should ask is:

We get a lot of feature requests and they aren't always valid. What would you do if you got a feature request to add a blue button?

Allow interviewees to show the skill if you can. Theoretical scenarios often just end up only showing red flags. Play acting is the better option. For example, if you want to know if someone gives kind feedback, give them some bad code and have them review it. If you want to know how they handle conflict, play act with the two interviewers coming up with conflicting ideas and ask them how they would resolve it.

It is the job of the interviewer to give the candidate the opportunity to show off. Interrupt (politely) if needed. You will be doing them a favor! Here are some possible polite interruptions:

  • I like where you are going with this but....
  • I'm sorry to interrupt, but I'm really curious about...
  • This is interesting, but I really want to hear more about...

You should also be sure to set an agenda and share rubrics with other interviewers ahead of time.

Pairing: A Guide To Fruitful Collaboration

André Arko gave this talk on the best way to pair and, as someone who has paired incorrectly for a while, it was quite interesting. So the basis of pairing is two devs, one machine.

I can't not use this gif even though pairing is not this.

Anyway! You should be actively collaborating. The best way to think of pairing is to think of it as one little meeting. If done right, it should push you to be a better dev and away from bad habits. Above all, pairing needs trust. If you are condescending, that breaks the trust of your pair and makes you a lousy pair. One good way to pair is to have the driver write a test, codes until the test passes, writes a new test, then switch driver to the other person, who then repeats the process. Never say "let me do this quickly by myself." That is not pairing! Help the driver solve the problem and stay on the same page, so you both understand. There's a lot more to this talk, but I think you should watch it yourself 😃

The Practical Guide to Building An Apprenticeship

Megan Tiu built out the apprenticeship program at CallRail and so we get to learn from her experience! To start an apprenticeship program, you need:

  • plan (what are they going to do?)
  • cash (pay them!)
  • buy-in (convince the boss!)

You can sell it by noting that apprenticeship programs:

  • eliminate onboarding costs (you get to teach a newbie developer your way of doing things)
  • eliminate recruiting costs (why pay a recruiter $10K when you can give it to your apprentice)
  • easier to hire seniors (who love to mentor)

Here's what you want to know about your plan:

  • How long will the program be? (suggestion: 3-4 months)
  • How many apprentices do you want to have? (ensure there are enough seniors to mentor them)
  • What should they know prior to starting? (do you expect them to have a basic working knowledge of Rails?)
  • What should they learn?
  • How will they learn it? (through tickets, a big project, pairing, etc)

For hiring your apprentices, you want an application (basic questions to get to the heart of what they are about), a code challenge, and a final interview. If possible, do end-to-end anonymization until they get to the final interview. You also want to ensure you have a rubric prior to starting this process. After you hire them, try giving lessons on foundation concepts, then give them small changes (bugs/internal code). Then rotate them around to different teams, including customer facing product. And don't forget to set early expectations!

Eileen's keynote

Eileen Uchitelle totally pumped me up. She discussed the various ways she is looking to make Rails more scalable by default. One of the things that really stuck with me was when she mentioned how so many companies are doing these things individually... so why not make them part of the overall framework and share the knowledge!

The Code Free Developer Interview

Can you tell I am into interviewing? This was a talk by Pete Holiday, also from CallRail. Here are the problems with coding during interviews:

  • don't replicate real work
  • disadvantage people without free time (code challenges)
  • live coding is very stressful, even for experienced people
  • difficult to develop and maintain a good code challenge
  • many passive candidates won't do the takehome (I've done this before)

So what's the solution? The primary solution is to just talk to candidates.

  • Ask all the candidates a consistent set of questions
  • Define a rubric ahead of time
  • Write down thoughts right after the interview

That's it! But there's more. Here are three possible techniques for a code-free interview:

1. Dig into their experience. Let them direct you to what they feel is most important. Ask questions like:

  • What was your role in the project?
  • How does the feature work?
  • What's the worst technical debt? Why hasn't the team fixed it? How would you fix it?
  • Has it had any bugs/outages in production? What happened? How did the team fix it?

2. Have them do a code review. If you choose this, make sure you are not using production code (they will have no context), are actively reducing complexity, and include realistic bugs without making it a bug hunt. One good option is to have a completely contrived situation with a simple application and a pull request to that simple app. Another is to fork an open source repository and create a contrived PR. The pull request should include no detail in the commit message, unsquashed commits, non-idiomatic code, overly complex, bad variable names, and actual bugs.

3. Try doing a collaborative system design. For this, you want to hypothetically build a tool, platform, or a project. You don't want any code or pseudocode and you should be working with the candidate. The general idea should be easy to understand and either related to the skills you're hiring for or well known. This can be forever-long, so it needs to be timeboxed. Let the candidate lead and build complexity if it's needed. For example:

Let's say we want to build Facebook. Get rid of the boilerplate (we already have users) and then ask "How do we implement status updates?". Once they get there, we can go deeper and ask about privacy controls, then granular privacy controls, and past that potential performance problems.

I loved this talk because I think code-free developer interviews should be the norm and have also been advocating for it at companies that I have been at.

Plays Well With Others: Improv For Nerds

H. Wade Minter gave this workshop and I don't have any notes on it because it was an improv class. But! One of the big things I took from it was our last activity. To remove bias from ideas, we did the following:

  • each wrote down an idea on how to improve RailsConf for next year
  • exchanged that idea with another person
  • each paired up with someone else, compared ideas, and gave each idea a number of points (total points for the two ideas could not be higher than 7)
  • exchanged ideas with a different person
  • wash, rinse, repeat until we have compared ideas 5 times

At that point, we had seen about 10 different ideas (plus our own) and the best idea could have a total score of 35 with the worst having a score of 0. Our top idea had a score of about 26, with a good number being around 22. We had a couple of bad ideas in the double digits (I'm looking at you, bacon table). This definitely seems like a good practice for any organization with a decent number of people.

And that's it...

I did some more, but I don't have any notes! I also sat and watched my friend Sam Phippen pair for an hour and a half, so if you want to learn yourself some RSpec, watch here!

Introduction to Scala

I signed myself up to teach a Scala class through Girl Develop It Pittsburgh a few months ago and the class was supposed to be tomorrow. I say "supposed to" because we only had two people sign up, so we ended up canceling. However, I still made a presentation! And since I spent all that time on a presentation, I decided to make a set of screencasts to accompany that presentation. If you've ever been interested in trying out Scala, I hope this helps. If you need any help or want me to go through some other aspect of Scala, feel free to contact me.

Find the rest of the series here.

Getting Started With Play After Working In Rails

I've now been using Scala since November (so a little over 4 months) and Play since January (exactly two months today). When I first started writing this application, I was brand new to Scala. My boss recommended Scalatra since he had some experience. Since I had none, I agreed and got started. I learn by example, so I first went through and found some projects that I could look at and base my project off. With Rails, this was easy. The Rails Guides are FANTASTIC (I miss them so much). With Scalatra, this was much more challenging. I made some progress, but then I came to a screeching halt, which caused my boss to post to Reddit asking for suggestions. Lemme pull out some of my favorite comments:

On stack overflow there are around ~250 questions tagged with scalatra. There are around 15k play framework related questions. You're pretty much on your own if you go scalatra.
Akka HTTP you pretty much have to have a PhD to understand.
Play lacks a coherent, functional API, documentation for a good 60% of it, and completely lacks the composability and ease of use of alternative frameworks like http4s. Most of these problems with Play are due to poor planning, and being a Lightbend technology which is contorted to work with Akka(and akka-http), yet another poor Lightbend tech. It's a pervasive rot in the community, just like Akka.

GREEEAAAAAAATTTTT. Anyway, we decided to try Play It has documentation (the bar, it is low), at least one book written about it, and some decent templates. I migrated my project over to Play and got going. One of the major differences I noticed between Play and Rails is that Play is not very opinionated. In general, if you look at a Rails project, everything is generally in the same place. Pretty much everyone uses ActiveRecord and the RDMS you choose doesn't really matter. With pretty much any Rails project, you can initialize the database with rake db:create. This is not the case for Play. As far as I can tell, you have to create the database and then Play will run evolutions (migrations). The real problem I have is that there also is no standard. Slick is very popular, but we decided to use the newer kid in class, Quill. And I couldn't find a single example of someone using Play 2.6, Quill, and PostgreSQL. And Play 2.6 is a breaking release from Play 2.5. I found one template that used Play 2.5, Quill, and PostgreSQL, but it broke when I upgraded to Play 2.6. Right now I'm having some database connectivity issues, but I'm hoping to resolve those soon. As soon as I get the app working, I'm going to create a template so hopefully, others won't have as hard of a time as I have.

Overall, I sorta wish I was still working in Rails? I love the simplicity of Ruby and how easy Rails makes it to get a decent CRUD app up and running. It definitely would have only taken me one week to make this app in Rails and it's taken four months (and counting) in Scala.

The Fuck: The CLI App You Didn't Know You Were Missing

Everyone has days where they constantly mistype things and their muscle memory is failing them. Enter fuck. It's a CLI app that allows devs to type out what they are actually thinking to get the command that they actually want. If you mistype a command (like chiwn instead of chown), all you need to do is type fuck next and it will correct your command and then run it. Here's an example gif:

out.gif

Option and Either in Scala

I'm used to Ruby. In Ruby, you can use nil with abandon and just do something like if variable to check if it exists. Below is valid Ruby code (though forgive me if I'm now out of practice):

1
2
3
4
5
6
7
def displayUser(user)
  if user
    user.name
  else
    "No name"
  end
end

When I started on this project, I started treating Scala the same way. However, I found out that apparently you want to avoid using null in Scala. My first iteration prior to discovering this was the following:

1
2
3
4
5
6
7
def displayUser(user: User = null): String = {
  if(user != null) {
    return user.name
  } else {
    return "No name"
  }
}

This is not proper Scala. Unlike Ruby, Scala has Option. Option allows you to have Some or None. As you might expect, Some has a value, while None is the equivalent of null. Here's an example of how to do that same function properly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def displayUser(user: Option[User] = None): String = {
  if(user.isDefined) {
    return user.get.name
  } else {
    return "No name"
  }
}
// call it like this
user = User()
displayUser(Some[user]) //or
displayUser()

Either provides a similar function to Option but is better for returning error messages. The problem I was trying to solve was creating an organization user. For that to happen, there must be an organization and a user. Here's my inital way I did it, thinking as a rubyist:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def validateUserAndOrgExist(email: String, orgId: Long): (Organization, User, Boolean, String) = {
  val user = usersService.findByEmail(email).head
  val organization = organizationsService.find(orgId).head
  if (user == null) {
    return (null, null, false, "User does not exist. Please create user first.")
  } else if (organization == null) {
    return (null, null, false, "Organization does not exist. Please create organization first.")
  } else {
    return (organization, user, true, "both exist")
  }
}

However, the better option (hehe) is to use Either in this case. So here's the better way to do this same function:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
trait ValidationError { val message: String}
case class IdentifierNotFound(message: String) extends ValidationError

def validateUserAndOrgExist(name: String, orgId: Long): Either[IdentifierNotFound, (Organization, User)] = {
  val user = usersService.findByEmail(name).headOption
  val organization = organizationsService.find(orgId).headOption
  if (user.isEmpty) {
    Left(IdentifierNotFound("User does not exist. Please create user first."))
  } else if (organization.isEmpty) {
    Left(IdentifierNotFound("Organization does not exist. Please create organization first."))
  } else {
    Right((organization.get, user.get))
  }
}
// then call it and use it like this:
val validationOfUserOrg = validateUserAndOrgExist("test", 123)
if(validationOfUserOrg.isRight){
  val (organization, user) = validationOfUserOrg.right.get
  print("User $user.name is in Organization $organization.id.")
} else {
  print(validationOfUserOrg.left.get.message)
}

And that's how you use Either and Option!

The TL;DR on tldr

There's a new package out that simplifies man pages and it's GREAT: tldr. Here's an example:

Screen Shot 2017-11-30 at 8.23.14 AM.png

tldr doesn't include every command, but it's growing with community support. Install it one of the following ways:

npm install -g tldr
brew install tldr
gem install tldrb
pip install tldr

There are many more ways to install, so if one of those doesn't work for you, go to the tldr github and find one that does.

Some Thoughts On That Sexist Memo

Yeah, just another hot take on that sexist Google memo. I could only read bits of it because it was just such garbage that I didn't want to waste my time on the whole thing. I also read a few other takes, but it overall seems like standard garbage and it doesn't really surprise me that a senior engineer at Google thinks this way. One of the bits that really stuck out to me was this:

We always ask why we don't see women in top leadership positions, but we never ask why we see so many men in these jobs. These positions often require long, stressful hours that may not be worth it if you want a balanced and fulfilling life.

This just sorta pissed me off because it's ignoring that most women don't opt for these jobs because sexism forces them to do almost all the household labor. Maybe it's also because, as a man, you can have what society deems as a "balanced" life while working long hours because your wife is taking care of the kids and a man isn't considered a bad dad if he's not super involved in the kid's life. This is total crap and bad for women and men that people think this way.

The real downside is that tech is the perfect industry to help even this out since it's pretty easy to work from home in most jobs. In one of my first dev jobs, one of the senior engineers that I worked with worked from home twice a week after his kid was born, alternating days with his wife, so she could go back to work too. I thought that was pretty cool and not a freedom most industries have! Maybe this is a faulty assumption that I'm making since I'm not a parent, but I'm assuming if you have kids you want to spend time with them?

Asking Questions

I've been thinking about this for a while, but keep not actually writing this post. One of the biggest mistakes I see juniors make is not to ask questions when needed for fear of looking like they don't know what they are doing. Granted, part of that is the way senior developers often react to questions. April Wensel wrote a fantastic article last August about the toxic tone that is prevalent in tech. So two points here:

  1. Senior devs should all read that article and consider more carefully how they talk to junior devs (or any other person for that matter). I'm not picking on anyone - I have definitely been guilty of this as well. However, being able to explain concepts plainly and empathetically shows your knowledge more than making someone feel dumb because they don't also have that knowledge.
  2. Junior devs need to make sure to timebox themselves. Give yourself a chance to do some googling, see if you can find an answer to your question on your own. However, after that first 30 minutes/hour, you should bring your question to someone else. Ideally, you have someone that you can approach who will answer your question compassionately. Make sure you give them all the information you have and the attempts you have already made. This will help avoid feeling like you are getting repetitive information.

Getting SQL Results That Are Distinct Across Two Columns

So this is a weird issue I just came across. Here's an example table schema:

mysql> describe queues;
+--------------+---------------+
| Field        | Type          |
+--------------+---------------+
| id           | int(11)       |
| customer_id  | mediumint(9)  |
| request_time | decimal(12,0) |
| item_id      | smallint(6)   |
+--------------+---------------+

mysql> select * from queues;
+------+--------------+--------------+--------+
| id   | customer_id | request_time | item_id |
+------+-------------+--------------+---------+
| 6829 |       15066 | 201704161118 |       1 |
| 6872 |       15066 | 201704161118 |       2 |
| 6875 |       15066 | 201704161118 |      26 |
| 6880 |       15066 | 201704161118 |       8 |
| 6881 |       15066 | 201704161118 |      15 |
| 6930 |       15077 | 201704161942 |       6 |
| 8683 |       14625 | 201704171412 |      10 |
+------+-------------+--------------+---------+

In my example, I might have the same customer requesting multiple items at the same time. I want to display all the items they have requested in the same line. That means I want to get a list of all the unique customers and request times combined. Yes, this isn't the *greatest* example because this table should probably be designed in a different way, but stick with me!

If I only want customer_id and request_time, that is pretty simple.

mysql> SELECT DISTINCT customer_id, request_time FROM queues;
+-------------+--------------+
| customer_id | request_time |
+-------------+--------------+
|       15066 | 201704161118 |
|       15077 | 201704161942 |
|       14625 | 201704171412 |
+-------------+--------------+

However, in my case, I need the queue id to do additional queries. That's where it gets just a smidge bit more complicated! Instead of just a simple DISTINCT, I've got to count the distinct records and then use HAVING to actually limit it.

mysql> SELECT *, 
COUNT(DISTINCT customer_id, request_time) as unique_orders 
FROM queues 
GROUP BY customer_id, request_time 
HAVING unique_orders >= 1;
+------+-------------+--------------+---------+
| id   | customer_id | request_time | item_id |
+------+-------------+--------------+---------+
| 6829 |       15066 | 201704161118 |       1 |
| 6930 |       15077 | 201704161942 |       6 |
| 8683 |       14625 | 201704171412 |      10 |
+------+-------------+--------------+---------+

Not too difficult, but I did go through a few different variations before getting to this result. I wanted it to work, but SELECT id, DISTINCT(customer_id, request_time) definitely does not!

Squashing Commits & Keeping Your History Clean

I'm a big fan of committing early and often. However, if you are anything like me, that means your commit history looks something like this:

added feature
fixed typo
oops another typo
added tests
fix failing test
fix another failing test
UGH TYPO

Fine for me alone, but not a great reference for the rest of the team when they try to figure out WTF I was doing a month or a year later. I've already written about how to rebase and squash commits before, so I won't cover that again. I do want to go a little more into why it's important to do so. Each commit message should reflect a distinct piece of work done. What I need to do now is rebase and change my commits to be more like this:

Added endpoint to return list of components
Added unit tests for component index endpoint

Now, if someone does a git blame, they can get the full context of what I was doing, not just a one character typo change. It's also worth expanding out your messaging and putting more context in the description. Every team has their own style and rules, but, personally, this is my normal git workflow and I'm a huge fan.

Finding Unseen Unicode/ASCII Characters in Ruby

This morning, I thought I was losing my mind. I'm writing a little web app (mostly Angular) that makes API calls. I know the API works, but for some reason, the calls from my app to the API were getting a 500 error in response. I tailed the API logs to see an "ArgumentError: argument out of range". However, the only thing that happened on this line was the date parsing. I open up the Rails console and start debugging. First I type out the date that isn't working. It works. Then I copy and paste from my browser. Failure.

irb(main):027:0> "2017-02-13T13:12:51Z".to_time(:utc)
=> 2017-02-13 13:12:51 UTC
irb(main):028:0> "2017‑02‑13T13:12:51Z".to_time(:utc)
ArgumentError: argument out of range

As you can see above, they look IDENTICAL. One of my coworkers suggested that I check the ASCII value of each character. Lucky for me, Ruby makes this easy.

"2017‑02‑13T13:12:51Z".each_byte do |c|
  puts c
end
==>
50
48
49
55
226
128
145
48
50
226
128
145
49
51
84
49
51
58
49
50
58
53
49
90

If you look at a chart of ASCII characters and values, you can see that 127 is the end of the standard characters. My fifth character starts with 226. I know that the pattern of 226, 128, 145 repeats twice and in the same spot as the dash. Looking at a UTF-8 encoding table, I can see that set of characters represents the non-breaking hyphen, which is definitely breaking my API call. Mystery #1 of the morning? Solved.

Creating Awesome Documentation With Yard

This semester I have been taking a computer architecture class. Overall, it's been pretty fun because I was given three projects and allowed to do them in the language of my choice. I chose Ruby. I'm pretty proud of these projects, so I decided to post them all to Github. If you are interested in the actual code, you can find it here. While I was doing this, I realized I needed to up my average documentation game. I needed the grader, who didn't know Ruby, to be able to easily understand what I was doing and why I was doing it. For the first two projects, I just wrote up documentation in a relatively reasonable way, and they were able to read through the code comments to see how it worked.

That's when I found YARD. YARD uses markup (I used markdown) and tags to create delightful HTML docs. What were just comments in my code turned into this, with almost no extra effort. I'd heard of it before, but I hadn't had a project that was worth massive documentation. YARD made the documentation a delight. You create the necessary documentation by using markdown, so your README functions as the homepage for your docs. Then, within each class, you use tags to explain params, return values, and add notes and examples. Here is an example from my MIPSDisassembler project:

class MipsDisassembler
  # Creates a new instance of MipsDisassembler
  # @param array_of_instructions [Array] the array of string instructions
  # @param starting_address [String] starting address for instructions, should be a string hexadecimal value
  # @param is_hex [Boolean] true if array of instructions is in hex, false if in binary
  # @note Each object in array_of_instructions should be a string representation of either binary or hexadecimal number
  # @return [MipsDisassembler] a new MipsDisassembler object
  # @example Create an object
  #    mips = MipsDisassembler.new(["0x022DA822", "0x8EF30018", "0x12A70004"], "7A060", true)
  def initialize(array_of_instructions, starting_address, is_hex)
    @instructions = array_of_instructions
    @starting_address = starting_address
    @is_hex = is_hex
  end

  # Takes binary/hex instructions and starting address and return an array of MIPs instructions.
  # @note Determines if r-format or i-format and parses accordingly.
  # @return [Array] an array of MIPs instructions, human-readable
  # @example Disassemble instructions
  #    mips.disassemble => ["7A060 sub $21 $17 $13", "7a064 lw $19, 24 ($23)", "7a068 beq $7, $21, address 0x7a07c"]
  def disassemble
    disassemble_instructions(@instructions, @starting_address, @is_hex)
  end

  # write MIPs instructions to file
  # @note File "mips_results.txt" will be created in same directory as code
  # @return [void]
  def output_to_file
    File.open("mips_results.txt", "w") do |f|
      in_file = ""
      disassemble.each { |instruction| in_file << instruction + "\n" }
      f.write(in_file)
    end
  end
 end

You can see the result of this code here as well as the image below. The result is easy to navigate documentation that you can share with anyone. It also gives the ability to see the source code of each method inline, so you don't have to go far to see the actual code behind public methods that you would want to use. I know I'm a bit of a dork, but I seriously loved putting this documentation together and I'm hoping it made my code just a bit more accessible.

Fun with Binary & Hex in Ruby!

While I haven't written a coding post in three months, I swear I do code every day. Recently, I started taking night classes again. This semester, I'm taking Computer Architecture and Data Structures with Java. My first project in Computer Architecture was to build a MIPS disassembler. I decided to use Ruby, which ended up bringing up some unique issues, mostly because Ruby does not have a short variable type within it's Numeric class. In Java, the short type is a 16-bit signed two's complement integer. Ruby does not use primitive types because everything has to be an object. No short object == no short type. Also, while binary and hexadecimal numbers can be converted easily to decimal in Ruby, they are initially strings. What does this mean? It means that in addition to the other parts of the translate, I also had to convert from hex to binary and from binary to signed decimal. I'll probably share all of my code in the future, but for now, here's a walkthrough of those two functions:

Translating to binary

This was pretty simple. I just had to use sprintf and it immediately converted the hexadecimal numbers into binary. Only one hitch! I needed the leading zeros (if there were any), so I had to use rjust to make sure it was a 32 bit binary by padding it to the left with 0s.

# takes an array of hex and returns an array of binary
# (32bit, including leading zeros)
def translate_to_binary(array_of_hex)
  array_of_binary = []
  array_of_hex.each do |num|
    array_of_binary << sprintf("%b", num).rjust(32, '0')
  end
  array_of_binary
end

Converting to a signed integer

Since I couldn't just cast as a short, I had to use two's complement. With two's complement, I knew that if the integer version of the binary was greater than 2^15, then it was actually a negative number. Otherwise, it was correct as is.

def convert_to_signed_binary(binary)
  binary_int = binary.to_i(2)
  if binary_int >= 2**15
    return binary_int - 2**16
  else
    return binary_int
  end
end

Boom! I hope this helps someone else who might've had the same trouble I did at first. I'll go into the program in full after my whole class has actually submitted theirs. 😛

SQL is the best!

I gave a SQL tutorial at PyLadies Boston last night and it was pretty fun. We used sqlite3 (which is definitely my least favorite DBMS, but it does come installed on pretty much every Linux/Unix machine by default and is the default for Django so I decided it was the best tool for this particular job. Giving a tutorial on something I used daily and have used consistently for 7 years was a bit weird because I did forget a few things because it didn't even cross my mind that people wouldn't know. For example: I initially neglected to mention that every statement needs a semicolon at the end and that you can't mix quotes (no " with '). Consider that was the bulk of all the issues, I'm feeling pretty successful right now! Take a look at the full tutorial below and let me know what you think.

Plotting Data From A CSV with Matplotlib

After I got all that data from the logs, my boss wanted it in a nice graph. First of the active user numbers, then the top 15 users. I knew that, despite having never used Matplotlib, it will still take me less time to learn it than any of my other options. I was able to get my script running and plotting correctly in less than two hours, so I felt pretty good about that. However, I had a few nested for loops and I wasn't a big fan. Enter the crowd-sourced code review! My friend Jenny was able to come up with a cool alternative to my solution that I ended up using. She utilized plot_date to sort the dates/data, which really helped (I was doing all sorts of crazy fun things).

So here's an example of what active_users.csv looked like:

system,au1,au30,date
jira,5,20,2016-06-09
confluence,16,23,2016-06-09
jira,8,22,2016-06-10
confluence,18,26,2016-06-10
jira,10,22,2016-06-11
confluence,18,26,2016-06-11
jira,11,23,2016-06-12
confluence,19,27,2016-06-12
jira,13,24,2016-06-13
confluence,19,28,2016-06-13
jira,8,24,2016-06-14
confluence,10,28,2016-06-14
jira,9,26,2016-06-15
confluence,15,30,2016-06-15
jira,15,26,2016-06-16
confluence,20,30,2016-06-16

he biggest problem was determining how to store the data in the program in a way that could be easily plotted. End solution? A dictionary of arrays. Or more precisely, a dictionary of a dictionary of arrays. With each line, we appended each data point to the matching array, which meant that a given date had the same index as it's data. And boom! It works!

import matplotlib.pyplot as plt
import csv
from datetime import datetime

active_users = {
  'jira': {
    'dates': [],
    'au1': [],
    'au30': []
  },
  'confluence': {
    'dates': [],
    'au1': [],
    'au30': []
  }
}

with open('active_users.csv') as csvfile:
  active_users_csv = csv.reader(csvfile)
  for system, au1, au30, date in active_users_csv:
    active_users[system]['dates'].append(datetime.strptime(date, '%Y-%m-%d'))
    active_users[system]['au1'].append(au1)
    active_users[system]['au30'].append(au30)

plt.plot_date(active_users['jira']['dates'], active_users['jira']['au1'], label='jira au1', color='orange', fmt='-')
plt.plot_date(active_users['jira']['dates'], active_users['jira']['au30'], label='jira au30', color='red', fmt='-')
plt.plot_date(active_users['confluence']['dates'], active_users['confluence']['au1'], label='confluence au1', color='green', fmt='-')
plt.plot_date(active_users['confluence']['dates'], active_users['confluence']['au30'], label='confluence au30', color='blue', fmt='-')
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
           ncol=4, borderaxespad=0.)
plt.show()
 Resulting graph of AU1 and AU30 numbers

Resulting graph of AU1 and AU30 numbers

Ok, so now that graph #1 is done, I had to graph the top 15 users over the past week and their usage patterns. First off, here's an example of the data I was working with:

User,Date,Request Count
jsmith,2016-06-20,12
kthrace,2016-06-20,1
shastings,2016-06-20,11
sbristow,2016-06-20,3
jmccoy,2016-06-20,3
akoni,2016-06-20,9
gmorrison,2016-06-20,4
pfisher,2016-06-20,18
ndrake,2016-06-20,10
lbriscoe,2016-06-20,7
egreen,2016-06-20,13
crubirosa,2016-06-20,20
avanburen,2016-06-20,2
mlogan,2016-06-20,18
ckincaid,2016-06-20,11
rcurtis,2016-06-20,21
jfontana,2016-06-20,16
clupo,2016-06-20,5
kbernard,2016-06-20,7

Obviously, with our actual prod data, there were thousands of users... so a few more lines to loop through. The first problem was to put the data into a format I could use. Since even a top user might not use the system at all one day (say a Sunday), I couldn't use a simple dictionary; this time I had to utilize defaultdict. Defaultdict enabled me to create a dictionary of users where the value was (by default) an array of 7 zeros (representing usage for the past 7 days). After that, I was able to loop through the file for each day. To get the file names, I had to start with yesterday's date and go backwards. The date still gets appended to the 'dates' array, but the big change is in users: instead of appending the data to an array, I insert it into the index that matches that day.

So now that I have a dictionary of dates and users, I have all that I need to determine the top 15 users of the week. I create another dictionary that has the users as keys and sums up their total requests from the array and sets that as the value. Once I do that, I sort it, end up with a tuple, reverse it, then slice off the top 15. At that point, I just need to loop through my weekly_active_users list and then plot each user's data! Though I did have one, final (much smaller) problem: I had to find 15 matplotlib colors that I could use and distinguish. I created my array of colors and added a counter to each loop so I could add a unique color to each user. Success!

import matplotlib.pyplot as plt
import csv
from datetime import datetime, timedelta, date
from collections import defaultdict
import operator
from itertools import islice
import re

active_users = {
  'jira': {
    'dates': [],
    'users': defaultdict(lambda: [0] * 7)
  },
  'confluence': {
    'dates': [],
    'users': defaultdict(lambda: [0] * 7)
  }
}

active_users_weekly = {
  'jira': {},
  'confluence': {}
}
current_date = date.today()
day = timedelta(days=1)

for i in range(0,7):
  current_date = current_date - day
  current_date_txt = current_date.strftime('%Y-%m-%d')
  for system in ['jira', 'confluence']:
    active_users[system]['dates'].append(current_date)
    with open("active_users_{0}_{1}.csv".format(system,current_date_txt)) as csvfile:
      users = csv.reader(csvfile)
      for user, log_date, request_count in users:
        active_users[system]['users'][user][i] = int(request_count)

sorted_active = {'jira': {}, 'confluence': {}}
for system in ['jira', 'confluence']:
  for user, request_list in active_users[system]['users'].items():
    active_users_weekly[system][user] = sum(request_list)
  sorted_active[system] = sorted(active_users_weekly[system].items(), key=operator.itemgetter(1))
  sorted_active[system].reverse()
  sorted_active[system] = list(islice(sorted_active[system],15))

fig = plt.figure()
jira = fig.add_subplot(211)
jira.set_title('JIRA')
conf = fig.add_subplot(212)
conf.set_title('Confluence')
colors = ['red', 'gold', 'darkorange', 'green', 'turquoise', 'dodgerblue', 'navy', 'darkviolet', 'violet', 'pink', 'darkslategrey', 'silver', 'blue', 'lime', 'orange']

i = 0
for user, request_num in sorted_active['jira']:
  jira.plot_date(active_users['jira']['dates'], active_users['jira']['users'][user], label=user, fmt='-', color=colors[i])
  i += 1

i = 0
for user, request_num in sorted_active['confluence']:
  conf.plot_date(active_users['confluence']['dates'], active_users['confluence']['users'][user], label=user, fmt='-', color=colors[i])
  i += 1

jira.legend(bbox_to_anchor=(0., 1.1, 1., .102), loc='lower center',
           ncol=8, borderaxespad=0.)
conf.legend(loc='upper center', bbox_to_anchor=(0.5,-0.1), ncol=8)
plt.show()
 Oooh pretty colors! Also, fake data makes for a bad graph.

Oooh pretty colors! Also, fake data makes for a bad graph.

Getting Started As A Developer

Through my work with PyLadies Boston, I have been asked quite a few times on how to get started with development. I'm going to try to write it all down here.

So you want to become a software developer?

Awesome! It's a pretty fun (albeit sometimes frustrating) gig and the pay is pretty decent too. Just be patient... it's not super easy and sometimes it'll get difficult. It's worth it though, so stick with it.

Step 1: Pick a language

Don't spend too long on this step! I would recommend either Python or Ruby as good beginner languages. The syntax is relatively similar to English, so it's not too hard to read code from early on. Also, these are two languages that are widely used at actual companies! Ruby is a fan favorite of startups and Python has a huge following in the scientific/academic communities. If you want to further progress into web development, I would recommend Ruby because, in my opinion, I think the documentation and tutorials available for Rails are much better (and in some cases easier to understand) than the docs/tutorials for Django.

Either way: don't think too hard about it. You just need to pick one. Once you learn one, you can always, much more easily, learn another.

Step 2: Pick a method

There are a load of resources out there. One I recommend is Zed Shaw's Learn Code the Hard Way (for Ruby, Python, SQL, and C). There's also How To Think Like A Computer Scientist (for Python), along with plenty of others. If you prefer a book, I can recommend both Dietel's How To Program (Java) and Pine's Learn To Program (Ruby, also a web tutorial!). The world of programming books/tutorials is  your oyster! Just pick a learning style that you like and stick with it. If videos are your thing, Codeschool has excellent video tutorials.

What I do not recommend: while Codecademy can be good for trying to decide what language to use, I do not recommend it for learning. Codecademy is software (what you will be building) and software has bugs. What you don't want to be spending time on is trying to figure out if the bug is yours or Codecademy's. If you think that sounds crazy, I have had Python code that I've run locally with no errors that gets a random error on Codecademy. Plus, one of the most difficult parts is installation and setup. You miss that with Codecademy. If this is your tool of choice, you have been warned.

Step 3: Give it some time

Try to dedicate some amount of time every day. 10 minutes when you first get in to work? 30 minutes when you get home? Doesn't matter. The more time you can dedicate, the faster you will progress, but the important thing is to make it a habit so you stick with it. Most of these resources have forums that you can utilize if you run into problems. If they don't, then you can also use StackOverflow. If you google for your error message, you will probably get a result on StackOverflow. Check it out and see if you can fix your bug. Once you get past the basics, give yourself a challenge by trying some exercism.io problems. They have problems for almost all languages and your submissions will actually get code reviewed!

Step 4: Level up!

You have a solid foundation! Time to take it to next level! And by that I mean web development. Is that the only route you can go? Nope! But I'm a web developer, so that's what I actually have experience on. Also, I have the most experience in Python and Ruby, so those are the languages that I'll have the most links for. If anyone has some next level topics for non-web developers, put it in the comments! Or link to your own post. Depending on what you started with, here are some resources:

Ruby:

  • Michael Hartl's Rails Tutorial - This is the best Rails tutorial out there. I'd almost argue that it's the best web dev tutorial out of any language.
  • CodeSchool's Rails For Zombies - If you prefer videos, Rails For Zombies is corny, but pretty great. And the first course is free!
  • Sinatra - a microframework for Ruby. If you really want to dig in and try to learn how things work, using a microframework that doesn't enable all the bells and whistles by default is awesome. 

Python:

  • Tracy Osborn's Hello Web App - Awesome book series made to teach non-programmers web development through Django
  • Getting Started With Django - Short video series. Starts you after the official Django tutorial
  • Django Book - The official Django tutorial. I'm hoping it's been updated since I tried to go through it because it was a bit buggy then.
  • Flask - a microframework for Python. Also see this tutorial
  • Lynn Root's NewCoder.io - Not web dev, but definitely a level up. Lynn has written tutorials on APIs, web scraping, data visualization, GUIs, and networks. These are great if one of these topics is of interest to you.
  • Daniel and Audrey Roy Greenfield's Two Scoops of Django - this is not really a beginner book. More an "after your first app" book. But this is one of the best programming books I have ever read, so I absolutely had to add it to this list.

Java:

  • Play Framework - As far as I can tell, this is the most popular web framework for Java. Their own documentation contains a solid amount of good tutorials to get you up and running fast.

Step 5: Build something!

This is absolutely the hardest step. Why? Because it requires you to actually be a little imaginative and think of something that you want to create. To start, you can create a website (either a personal site or a landing page for your project) on Github Pages. It's free and super easy to get started! As far as picking a project, there are shortcuts if your brain is a bit fried and you can't think of anything. There are lists of coding projects that you can pick from. You can also contribute to open source. Whatever you choose, the important thing is to keep working at it. Even senior developers are still constantly improving their skills, so you will constantly be learning at all stages of your career.

Calculating Age... in Java 8

I'm doing a bit more Java now that I'm taking a Java class. With that is coming a lot of "oh that should be easy... wait, there's not a really simple way to accomplish this???". First example of this: determining someone's age.

import java.time.LocalDate;

public class Age {
  private int birthYear, birthMonth, birthDay, age;

  public Age(int birthYear, int birthMonth, int birthDay){
    this.birthYear = birthYear;
    this.birthMonth = birthMonth;
    this.birthDay = birthDay;
    this.age = getAge(birthYear, birthMonth, birthDay);
  }

  public int getAge(int birthYear, int birthMonth, int birthDay){
    LocalDate fullBirthday = LocalDate.of(birthYear, birthMonth, birthDay);
    LocalDate now = LocalDate.now();
    long daysSinceBirth = now.toEpochDay() - fullBirthday.toEpochDay();
    return (int) (daysSinceBirth/365);
  }
}

LocalDate is new to Java 8. Previously it was part of the Joda-Time API, but the Java folks seem to have added the bulk of the functionality directly into Java. Sweet! What does this allow us to do? LocalDate creates an object that represents a date and has quite a verbose API. Since we're calculating someone's age, we are going to need an object that represents their birthday and an object that represents today (in this case fullBirthday and now). If we convert these both to Epoch Days, which is generally just the number of days from 1970-01-01, we can just compare the number of days and divide by 365 to get the age. Not too hard... but did take a second to come up with it... I was a bit surprised that it seemed like I couldn't actually subtract dates. Ruby has spoiled me...

Update: In the comments below, Ted Vinke alerted me to another, even easier way to calculated it with the Period. This solution still uses LocalDate, but takes less math on our part. Thanks for the improvement, Ted!

import java.time.Period;
import java.time.LocalDate;

public class Age {
  public int getAge(int birthYear, int birthMonth, int birthDay){
    LocalDate fullBirthday = LocalDate.of(birthYear, birthMonth, birthDay);
    LocalDate now = LocalDate.now();
    int period = Period.between(fullBirthday, now).getYears();
    return period;
  }
}