Larry Price

And The Endless Cup Of Coffee

Made to Stick

| Comments

Knowledge curses us, if we find it hard to imagine what it was like not to know it. And it becomes difficult to share our knowledge with others because we can’t readily re-create our listener’s state of mind.

The Gist

Made to Stick by brothers Chip Heath and Dan Heath tries to uncover why some ideas capture our imagination while others fall into the abyss, never to be seen again.

My opinion

I really enjoyed this book. I tend to read very technical books, so this was a nice change of pace. I coincidentally was reading this book while coming up with a ‘pitch’ for my recent SEP Startup Weekend project, Ollert.

Made to Stick lays out examples of really good and really mediocre descriptions of ideas. The authors then use the concepts discussed within the book to improve these ideas or discuss why they are effective. Whenever I hear people explaining real-world ideas, I start to think about the concepts laid out in this book. I’d like to be able to use these concepts to communicate my own ideas more effectively, but I have thus far found it to be easier said than done.

The book is not terribly long. This allowed me to connect all the concepts and better critique the numerous examples.

Although I did enjoy the book, a few of the sections came off a little bit like a self-help program. The concepts described in the book are ‘Simplicity’, ‘Unexpectedness’, ‘Concreteness’, ‘Credibility’, ‘Emotional’, and ‘Stories’, which “just happen” to spell out most of the word “success.” This silly feel-goodery acronym makes the cynical side of me cringe.

Who Would Like This

Anyone trying to communicate a new idea to someone else could benefit from this book. On a broader scope, this book can benefit your communication patterns in general. This is particularly helpful for professionals, who may have a difficulty expressing their ideas without getting too deep in the technicalities.

Using the Trello API in Ruby

| Comments

So, you want to gather data using the Trello API so that you can do something cool with it? And you’re using Ruby? Enter ruby-trello. Install!

1
$ gem install ruby-trello

We’ll start off easy, and assume that we’re writing a personal application where we only need to access data for one user at a time. We start by configuring ruby-trello. I’m going to assume that you’ve already generated a public key and received a member token and stored them in your environment.

global_config_test.rb
1
2
3
4
5
6
7
8
9
10
require 'trello'

Trello.configure do |config|
  # API key generated by visiting https://trello.com/1/appKey/generate
  config.developer_public_key = ENV['PUBLIC_KEY']

  # Member token
  # larryprice.dev/blog/2014/03/18/connecting-to-the-trello-api/
  config.member_token = ENV['MEMBER_TOKEN']
end

This connects me to a specific member as found through ENV['MEMBER_TOKEN']. I previously wrote another post about getting a member token from a user.

For demonstration, I’ll find myself, grab my first board, and then display the name, names of lists, members who have worked on the project, and some numbers about each of the cards in the board. This is essentially my proof of concept for a super-cool web-app I wrote called Ollert.

global_config_test.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...

# find myself
me = Trello::Member.find("_larryprice")

# find first board
board = me.boards.first
puts board.name
puts "Lists: #{board.lists.map {|x| x.name}.join(', ')}"
puts "Members: #{board.members.map {|x| x.full_name}.join(', ')}"
board.cards.each do |card|
      puts "- \"#{card.name}\""
      puts "-- Actions: #{card.actions.nil? ? 0 : card.actions.count}"
      puts "-- Members: #{card.members.count}"
      puts "-- Labels: #{card.labels.count}"
end

Wow, cool! Such data! This is really great for a single user because we only have to make the connection to Trello once (which is not incredibly fast). However, this won’t work in a multi-user environment since we configured ruby-trello to use a specific member token. So how do we connect to multiple members at a time? Let’s print out the same data we did above for a single user, but using Trello::Client to connect to Trello.

client_test.rb
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
require 'trello'

me = Trello::Client.new(
  :developer_public_key => ENV['PUBLIC_KEY'],
  :member_token => ENV['MY_MEMBER_TOKEN']
)

you = Trello::Client.new(
  :developer_public_key => ENV['PUBLIC_KEY'],
  :member_token => ENV['YOUR_MEMBER_TOKEN']
)

[me, you].each do |user|
  puts user.fullname
  board = user.boards.first
  puts board.name
  puts "Lists: #{board.lists.map {|x| x.name}.join(', ')}"
  puts "Members: #{board.members.map {|x| x.full_name}.join(', ')}"
  board.cards.each do |card|
        puts "- \"#{card.name}\""
        puts "-- Actions: #{card.actions.nil? ? 0 : card.actions.count}"
        puts "-- Members: #{card.members.count}"
        puts "-- Labels: #{card.labels.count}"
  end
end

Now, as your friend and teacher, I command you to use this knowledge to go do cool stuff with Trello!

Connecting to the Trello API

| Comments

Trello has a pretty sweet API, which we use extensively in our Trello-analysis app Ollert. Initially connecting to the Trello API took us a few hours, so I’d like to make a record of how we managed to connect.

Making a connection to Trello requires two hashcodes: an application key and a Trello member token. You can generate and view your application key by visiting https://trello.com/1/appKey/generate.

The member token is something we need to get from the user. There are two ways to get a user’s member token: through fragments and through a postMessage. You can also request different levels of access (read, write, read+write), and different expiration periods (such as 1 day, 30 days, or never) for member tokens. For the remainder of this writing, I’ll be accessing a read-only member token that never expires.

We didn’t have a lot of luck with fragments, but the concept is simple enough. You have the user click a link that probably says “Connect With Trello” which is similar to:

https://trello.com/1/authorize?key=applicationkey&name=applicationname&expiration=never&response_type=token

At this point, the user is redirected to Trello and given the opportunity to Allow or Deny your application access. Once allowed, the user sees a static Trello page with their member token in plain text. Somehow you"re supposed to convey to them that they should copy this token and paste it back to you. This has clear drawbacks in usability.

Using the postMessage method of accessing a member token was significantly more fruitful. Trello provides a Javascript file named client.js that does most of the legwork for you. An example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%script{src: "//api.trello.com/1/client.js?key=applicationkey"}

function AuthenticateTrello() {
  Trello.authorize({
    name: "YourApplication",
    type: "popup",
    interactive: true,
    expiration: "never",
    persist: true,
    success: function () { onAuthorizeSuccessful(); },
    scope: { write: false, read: true },
  });
}
function onAuthorizeSuccessful() {
  var token = Trello.token();
  window.location.replace("/auth?token=" + token);
}

%a{href: "javascript:void(0)", onClick: "AuthenticateTrello()"}
  Connect With Trello

When the user clicks the link, we have Trello set to activate a “popup” that will ask them to “Allow” or “Deny” our app from accessing their data. When the user allows us access, the popup closes and we hit the “onAuthorizeSuccessful” method. In my method, I simply redirect them to the /auth route with token manually added to the params list. One of the interesting options listed above is the “persist” option, which tells Trello whether it should prompt the user for his or her token every time. By telling Trello to persist, the user will only be presented with the popup when he or she needs to reauthenticate.

You can learn more about member tokens from https://trello.com/docs/gettingstarted/authorize.html.

SEP Startup Weekend: Ollert

| Comments

Last weekend was SEP’s 6th Semi-Annual Startup Weekend. For those unfamiliar, software developers pitch ideas Friday evening and developers volunteer their time to come up with a minimum viable product in the next 48 hours. Free beer is the only thing that makes such a weekend possible.

I’ve been before and participated in other people’s projects and it’s always a blast. However, this weekend was different: I had an idea to pitch. Although the idea went through many names, the core concept remained the same:

To tell Trello users what their boards say about the past and the future through unique visuals.

Trello is a collaborative workflow management tool that does a wonderful job of showing you the present. There is currently no way on Trello to see your past history or compare yesterday’s weather. This simplicity is part of the beauty of Trello, but also an opportunity.

My idea was to create a web application where a user could quickly and easily connect with Trello and view information he or she had never seen previously. I would offer a trial service requiring no login that would allow access to all this data, given that the user puts up with authenticating with Trello every time he or she visits the site. There would be a free membership, which would allow the user to “permanantly” connect to Trello. To monetize, I wanted to offer a paid membership, where the user would be given the ability to compare “historical” Trello data by selecting begin and end dates for the Trello data that is analyzed.

Ollert is the result of this Startup Weekend idea. A live version of Ollert can be found at ollert.herokuapp.com.

I worked on Ollert with 5 other great developers, and we got a spectacular amount of work done given that we only spent a single weekend programming. We were able to direct users to connect with Trello, let them select a board, and then generate and display 12 different statistics and analyses. We also implemented Sign Up/Login.

We worked on Ollert to the last minute, so not everything got in. We never implemented the paid member feature and we didn’t get in all the analytics we wanted. We also had some great ideas come out while we were working on Ollert that didn’t make it into the application, such as filtering chart types and selecting favorites.

Overall, my teammates and I had a great time and we are confident that we’ve created something useful.

My current intention is to do several more blog posts about Ollert including Connecting to the Trello API, Using ruby-trello, Using sqlite on Heroku, What I Should Have Had Ready Before Asking People To Work For Me, and The Future of Ollert.

Using X-editable to Do In-line Editing for You

| Comments

In-line editing is traditionally difficult. Taking a static HTML node and turning it into an editable text field and then sending that data off somewhere is a little bit more Javascript than I like to write. My team and I came up against a very difficult UX problem which I spent over a week trying to understand. After building and discussing several solutions, we eventually decided to narrow the scope of what the user should be able to change. We would present the user with a table where one of the columns would be editable.

Rather than showing a table with the third column always as a field for text entry, I used the Javascript library X-editable. X-editable allows me to display my editable item as a link. When the user clicks the item, the link turns into a text field with a Save/Cancel button (and a ‘clear’ button as a bonus). The ‘Save’ button submits an AJAX ‘POST’ or ‘PUTS’, where we are then allowed to validate and save the data. The ‘Cancel’ button turns the text field back into a link with the original value.

Hooray, someone else has already done the hard work for us! So, what I want to do is dynamically generate a table with objects and allow the user to edit one of the columns. In my case, only the end column is editable, but I could just as easily spread this availability to all my columns. I’m using all my favorite tools to create this page, specifically ruby, Sinatra, Twitter Bootstrap, and HAML.

X-editable has implementations using Bootstrap, jQuery UI, and jQuery. Since I’m already using Bootstrap in my app, I’m going to go with that version. Note that there are Bootstrap 2 and Bootstrap 3 builds of X-editable, and I’m using the Bootstrap 3 variety.

First I include the necessary files at the top of my HAML document.

/views/manage_data.haml
1
2
3
4
5
6
%script{src: "//code.jquery.com/jquery.js"}
%script{src: "//getbootstrap.com/dist/js/bootstrap.min.js"}
%link{href: "//getbootstrap.com/dist/css/bootstrap.min.css", rel: "stylesheet"}

%link{href: "//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/css/bootstrap-editable.css", rel: "stylesheet"}
%script{src: "//cdnjs.cloudflare.com/ajax/libs/x-editable/1.5.0/bootstrap3-editable/js/bootstrap-editable.min.js"}

I’m going to dumb my page down so I only have to show the important parts. I’m going to create a table from an array of hashes called @options. Each hash has two important fields for this table: Points and Dollars. I will iterate over @options, displaying the points field as plain text and the dollars field as an X-editable link. Each hash also has a value field containing the primary key for the object to be edited.

/views/manage_data.haml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.container
  .row
    .col-md-6
      %table{id: "edit_points_goal", class: "table table-responsive table-hover table-bordered"}
        %thead
          %tr
            %th{width: "35%"}
              Points
            %th{width: "65%"}
              Dollars
        %tbody
          - @options.each do |option|
            %tr
              %td{style: "height: 45px; padding: 4px 8px; vertical-align: middle;"}
                #{option[:points]}
              %td{style: "height: 45px; padding: 4px 8px; vertical-align: middle;"}
                %a{href:"javascript:void(0)", "data-type"=>"text", "data-pk"=>"#{option[:value]}", "data-url"=>"/update", "data-title"=>"Enter dollar amount"}
                  #{option[:dollars]}

The important part is the a tag:

  • href goes nowhere.
  • data-type tells X-editable how it will edit the presented data. According to the docs, types include text, textarea, select, date, checklist and more.
  • data-pk is the primary key of our data in the database. In my case, that comes to me stored in the value field of the option hash.
  • data-url is the post method that will be used to interpret the data.
  • data-title is used to tell the user what to do.

Our code won’t do anything yet seeing as it’s not connected. We need some Javascript to do that. I put the Javascript at the top of my file, underneath the included files. The first thing we need to do is tell X-editable what type of editing we’ll be doing. The options are in-line editing or a pop-up. I want the in-line editing in this case. The second thing I want to do is to set the editable attribute on the appropriate DOM objects. Since I’m using an array, I found the easiest way to do this was to start at the table’s id (edit_points_goal) and trace down to the a tag.

/views/manage_data.haml
1
2
3
4
5
6
7
8
:javascript
  $.fn.editable.defaults.mode = 'inline';

  $(document).ready(function() {
    if (#{vm.editable}) {
      $('#edit_points_goal tbody tr td a').editable();
    }
  });

Now we need to deal with the post request. If you’re really impatient to see things work, you should be able to see your in-line editable code in action, but the post call will fail with a “NoMethodError.”

Our post is going to be really simple. We verify that we have a non-negative integer and we either save and return 200 or we return 400 with an appropriate message. Our table in this example is just called Data, and we use find to get a value out of the database.

web.rb
1
2
3
4
5
6
7
8
9
10
11
post "/update" do
  data = Data.find(id: params["pk"])

  unless params["value"].match(/[^0-9]/)
    data = params["value"].to_i
    data.save
    return 200
  end

  return {400, [], "Please enter a valid non-negative number"}
end

Things should be working now. When you enter good data and click the ‘Ok’ button, our post will be called and the text field will turn back into a link. When you enter bad data, you should see our error message below the text field box.

Issues I encountered:

  • Table column width shifting - Fixed by setting the widths explicitly, as seen above (35% and 65%).
  • Table height shifting - Fixed by setting the style of the td as seen above to give a larger height, more padding, and aligning the inner objects explicitly. I would recommend moving this definition to a .css or .scss file.
  • “NoMethodError” after clicking Go - Unfortunately, if you made any coding errors in your route, you won’t be able to see the Sinatra error page but will instead you’ll be presented with a giant wall of HTML below the text box. Try to parse this error, but if you struggle to find out what went wrong you can always substitute a form-post method in place of the a tag, which may allow you to more easily figure out the problem.

The X-editable docs are extremely helpful for beginners. There is even more detail on the X-editable site, including editing multiple items, editing dates, and using a pop-up to edit the data.