Week 3 Round Up: Twitter Fitter—the tweet generator!

So there's still a lot more work I want to do on my week 3 app Twitter Fitter. But the general idea is there--it's a Markov generator. You type in a twitter handle, and it spits back a generated tweet (or tweet-like string) that is in the style of that user. (Try @DATABASE_HULK, @StephenAtHome, @BarackObama, or @kimkardashian) Part of the app/week project is that I want to learn something new each time. So what did I learn this week?

Firsts:

Well, this was my first app where I had to connect to another service (in this case it was the Twitter API through the Twitter gem) and also the first app where I wrote my own POROs (plain old Ruby objects) inside of Rails. In fact, this was barely a Rails app. It doesn't even have a model. It's mostly just a single page that connects to Twitter and then runs the MarkovChain object that I wrote for it. I also obviously learned a lot about Markov generators and how they work. (I knew about them generally but never thought about how to write one before)

Testing:

I was able to write tests for my MarkovChain object. I'm pretty proud of this. But I haven't written tests for the actual controller code yet (which is currently very messy).

To do:

As always there are still many things I want to do to improve it:

  • Needs major styling. Looks really ugly right now. I want it to look good!
  • I want you to be able to retweet the generated tweets. Maybe it will retweet it from a custom account, maybe @TwitterFitter
  • I want to make a gem out of the MarkovChain object. There are already similar gems out there (and my algorithm isn't all that unique), but I think it would be a learning experience to create my own gem.
  • Tweak my generator code. Currently it's pretty simple, and the results are either pretty funny / nonsensical or else they're pretty boring (like almost word for word of an existing tweet rather than a mix-match of different tweets)

Challenges:

  • writing the actual Markov generator, and making it perform okay as well as be open to modification in the future if I wanted to change the way it works later. For example, I thought it would be cool if I could pass it a lambda so that it picks the next word based on running that lambda code. The default is just random, but maybe someone would want to tweak it themselves without having to go into my code.
  • figuring out how to call private / public methods within my class, and whether or not to even use private methods. Here's a blog post I wrote about this topic.
  • tweaking the generator. When I use a prefix length of 2, I get back text that's very similar to the original text, so much so that it's kind of boring. If I set it to 1, I get back things that are sometimes very nonsensical. I decided on 1. But it would be nice to come up with a different algorithm so that it's between totally nonsensical and completely boring.
  • figuring out how to include my ruby object in the lib directory and have rails auto include it. I was not naming my file correctly, so it didn't include it when autoloading! Took me some extra googling to figure out why.
  • writing test code. I'm not sure I did it right, but testing code that's supposed to be random was also kind of tricky. I had to seed the generator with values so that it would be predictable/testable.
  • using the Twitter gem. I had to figure out how to give it my API keys and secrets without exposing it through github. I wrote a blog post about this here.

Next week(s):

I've mentioned taking the incredibly helpful edX.org SaaS class (Software as a Service) before. Well, part 2 of the class has just started, so instead of continuing with my own apps, I will be doing the SaaS homework apps every week for the next few weeks. It's only a few weeks, and there's not a homework for every week, so it won't be taking over this project completely, but its presence will definitely be felt. I will still be blogging about my discoveries, though! Hope you'll keep reading.

Drop some knowledge!

self or no self

Ruby is sometimes very confusing. For example, when you call an instance method within the same class, you can do so with or without self... it's optional:

instance_method # works from inside the class
self.instance_method # also works from inside the class
other_object.instance_method # works from outside the class

However, if the method is a setter method, you must use self. The reasoning behind this is that Ruby doesn't know whether instance_method is an instance method or a local variable, and the self gets rid of that ambiguity. Let's call this rule A.

instance_method = 42 # error
self.instance_method = 42 # works

However, private methods can't use self. Let's call this rule B. The reasoning behind this is that private methods cannot be called with an explicit receiver, even if self is that receiver.

self.private_method # error
private_method # works

What happens when rule A butts head with rule B?

private_method = 42 # violates rule A - error
self.private_method = 42 # violates rule B - but works!

Apparently in this case, the second line is the one that's allowed. This blog post explains it very clearly:

When using a setter in Ruby we're supposed to use an explicit receiver like self. Here we have a private method that doesn't allow an explicit receiver. We appear to have reached an impasse. In this case it turns out Ruby breaks its own rule.
Before I found this, I was trying to get around the impasse by doing jenky things like this:

private_method=(42)

I was surprised this wasn't a bigger topic, but then I read this article about how private and protected methods aren't even used that much in Ruby!

For the most part, we just... don’t [write private methods].
The following is from the book I'm reading, Practical Object Oriented Design in Ruby by Sandi Metz:
Users of a class can redefine any method to public regardless of its initial declaration. The private and protected keywords are more like flexible barriers than concrete restrictions. ... Using them sends two messages:
  • You believe that you have better information today than programmers will have in the future
  • You believe that those future programmers need to be prevented from accidentally using a method that you currently consider unstable
These beliefs may be correct but the future is a long way off and one can never be certain... many perfectly  competent Ruby programmers omit them and instead use comments or a special method naming convention (Ruby on Rails, for example, adds a leading '_' to private methods) to indicate public and private parts of interfaces.
I don't know what I feel about all this yet, but I might skip over writing private methods for now just because it makes things much easier for me immediately.

Drop some knowledge!

Hiding my API keys

Problem:

I don't want to expose API secret keys, but I still want to be able to commit my changes and push to github.

Solution:

  • Install figaro gem
  • set environment variables in config/application.yml
    my_api_key: 'weoi23urow234jerow67jerjerjwejr'
  • make sure .gitignore ignores config/application.yml
  • in the initializer code, (for my week3 app, I put this in config/initializer/twitter.rb) use ENV['my_api_key'] instead of hardcoding the api key
  • after deploying to heroku, run this command to set heroku's env variables

figaro heroku:set -e production

Drop some knowledge!

Week 2 Round Up: say-anything

Week 2 of An App Per Week is over! It was tough and I got a little behind. I planned to implement the RailsBridge intermediate curriculum, plus testing and some extra features. Here is what I was able to accomplish:

  • all the basic features specified in the curriculum
  • extra features:
    • deleting posts
    • profile page with a list of posts (not replies yet)
    • custom user fields: username and favorite movie fields instead of just the default e-mail and password
    • some testing
    • fancy bootstrap styling
What I wasn't able to finish this time around:
  • Testing! I got some testing done, but it was taking too long to do for features that I wasn't sure how to build out, so I figured I'd write the tests after the fact. So that's still on my to do list...
  • Omniauth -- being able to log in through Twitter, Github, FB, etc. I might do this tomorrow
  • fancier post markup, or maybe even a wysiwyg edit window would be nice (I'm sure there's a gem, right?)
And here it is! All in all, I am pretty happy with it. The major things I learned:
  • Devise was a pleasure to work with. It's relatively easy to set up and gave me a lot of standard user stuff out of the box. I really liked it. It wasn't even that hard to add custom fields (username, favorite movie, etc.) to the user profile. At one point I had to look into the Devise source on github to figure something out, but even that wasn't too hard because it's pretty well organized.
  • Nested resources. I implemented replies the non-nested way, then re-read the hints section, and figured out that nested resources was the way to go with this kind of feature. Nested resources gives you an url that corresponds to the relationship of your models. So because replies belong to posts, you can have a URL like post/5/replies. It doesn't matter as much in my finished product since I ended up embedding replies within posts anyway.
  • Dealing with strong parameters
  • has_many / belongs_to association -- I've dealt with this before, but this was my first time working with them outside of a tutorial, so it sunk in more
  • The asset pipeline -- had to figure some assets out and so I read a little bit about this, but I need to read way more when I have time

Unsolved problems:

When testing, I had to run some tests as a signed in user and some as signed out. How do I do this? After some reading, I figured out that I had to stub the authentication in the specs. I followed these directions and put the ControllerHelpers module in spec/support/controller_helpers.rb as well as the accompanying config. But it kept complaining: "undefined method sign_in".

After a certain amount of googling, head scratching, and generally going nowhere, I finally just moved the module into my spec file. Not graceful, but I had to keep going. If you have any ideas about this let me know.

Also while trying to test user authentication related things, I figured out how to fake sign in/out inside of rails console and get to the user object:

ApplicationController.allow_forgery_protection = false
app.post('/users/sign_in', {"user"=> {"username"=> "jimmy", "password"=> "mypassword"}})
cu = app.controller.current_user

I find it super useful to be able to try things out in the console. It's probably where I learn the most.

Another problem. In Michael Hartl's Rails Tutorial book, he was able to set up a has_many / belongs_to association between User and Micropost. And because this was set up, he was automatically able to write something like this:

User.micropost.some_method()

However, I set up my app in a very similar way. My :post belongs_to :user and my :user has_many :posts. But I couldn't get this to work. I always got "undefined method posts". My workaround was simply to use plain old Posts object and pass it the current_user when needed.

Another thing I learned... where and find_by_??? methods are not interchangeable:

x = User.where(id: 3) # returns ActiveRecord::Relation
y = User.find_by_id(3) # returns User
x.id # error, no id method
y.id # returns the user id correctly
x.each { block } # works
y.each { block } # error, no each method on User

There are many other smaller things I could blog about, but I don't have time and you probably don't have patience. I may write up a few of these smaller issues later if I feel like it.

Next Week

Any ideas? I'm thinking about writing an app that would involve pulling from some API... maybe the Twitter API and doing something with the data. Stay tuned.

Drop some knowledge!

How do I write tests that I don’t have to keep changing?

Sometimes I want to check to make sure my controller is sending a message to my model. For example, I want to make sure it's asking the model to return a set of results. But I don't care what message it sends:

Posts.should_receive(:all)
  get :index

Here I'm making sure I'm asking the model for all posts. However, I soon realized that I wanted them ordered a certain way. So I replaced the controller code to call :order instead of :all

Now I have to go back to the test and change it. But later perhaps I will want to change it again to use :find or :find_all_by or :where or something else altogether.

Will I have to change the test every time? What I really want is a way to say “make sure my controller sends my model a message, it doesn't matter what message it sends.” Because basically all these methods perform a similar task, and I don't care which implementation I ultimately decide on. My tests should just care about the fact that I sent the message at all, and that the model will take care of the rest. So my question is two-fold:

  1. Should I even be testing for this? Or is this not an important thing to test?
  2. If I should be testing for this, how do I do it so I don't have to change my test code every time?Is this possible?

Drop some knowledge!