Hey There!

Hey, I’m Steve!  I’m a Staff Software Engineer at AppFolio.  I love creating, tinkering, and being inspired.

My Public Projects:

headyversion.com: a growing community focused around discovering the best versions of Grateful Dead songs

kind.fm: discover carefully curated spotify playlists from the best minds in independent radio

If you’re interested in collaborating on a project or sharing knowledge, you can e-mail me at steve.klebanoff [at] gmail [dot] com.

TypeScript & React: Property does not exist on type IntrinsicAttributes

If you’re working on a codebase where you are trying to introduce TypeScript gradually, you may start renaming your React components from `.js` or `.jsx` files to `.tsx` incrementally.

Let’s say you are converting FormComponent (form_component.tsx), which imports and renders InputComponent (js file, not converted, input_component.js). If you have installed types for React, you may run into the issue when rendering <InputComponent myProp={someProp}>:


Property 'myProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<InputComponent> & Readonly<{ children?: ReactNode; }> & Readonly<{}>'.'

This is because at the time of writing, typed React defaults React components to have an empty property and state. To get around this, you can do any of the following:

– Convert InputComponent to a .tsx file. This takes some effort: converting props and state, and resolving any other errors. You will also have to do the same for any custom React components it imports.

– Define a .d.ts file for InputComponent. react-to-typescript-definitions can help with this. A disadvantage here is that your .js files and the .d.ts can get out of sync.

– Add the following code to InputComponent:

/**
* @augments {object, object>}
*/

The last option is the path of the least resistance, but you lose a lot of the cool type safety that doing the other options would provide. You can follow any updates to the @augments approach at the following github issue: TypeScript/issues/14558

Twitter OAuth Authentication with Elixir and Phoenix

There are a few good examples showing how to use OAuth authentication in Phoenix and Elixir, but I wanted a solution which used a small amount of dependencies, and had the ability to make authenticated requests on behalf of a user.

parroty/extwitter provides the functionality to authenticate as a user, as well as to make requests on behalf of that user. I wanted to just use this library, and avoid adding the additional dependencies of ueberauth and guardian.

Full code is available at: https://github.com/steveklebanoff/twitter_oauth_example

Here’s a breakdown of some of the interesting parts:

AuthController

We start off authenticating via twitter in the request function. This generates and redirects the user to a URL which will prompt them to authorize our application via twitter.

If the user authorizes, they will be redirected to our callback function with oauth information in the request parameters. We use ExTwitter to generate an access token based on oauth_verifier and oauth_token which was provided by twitter as URL parameters. We then configure ExTwitter to use the access_token and access_token_secret we received. This allows us to make verified requests on behalf of this twitter user.

Now that we can make verified requests, we call verify_credentials to ensure everything is kosher, and also to receive information about the user’s twitter account. We take a subset of that information (name and screen_name) and set it on the session as current_user.

We also store the access_token and access_token_secret on the session, so we can make further authenticated requests later. At the time of writing, twitter does not expire these tokens. Because Phoenix uses Plug.Session.COOKIE, which is “based on Plug.Crypto.MessageVerifier and Plug.Crypto.Message.Encryptor which encrypts and signs each cookie to ensure they can’t be read nor tampered with”, we can feel confident that this information will be safe.

Plugs

We define three plugs which help our controllers work. These plugs are similar to rails controller before_filter in that they are executed before controller code.

The first, AssignCurrentUser, just allows us to access current_user through conn.assigns.current_user instead of having to do get_session(conn, :current_user) every time.

The second, RequireUser allows us to limit certain controller actions to only be available to signed in users.

The third, ConfigureTwitterClient sets up our ExTwitter client to have the user’s access_token and access_secret configured for any twitter API call made by a signed-in user.

TweetController

With authentication already handled, and ConfigureTwitterClient already setting up ExTwitter for authenticated users, the TweetController is very small and simple. It uses ExTwitter to grab a user’s timeline, and to post a new tweet.

Putting It All Together

You can run the app see how the whole thing comes together in the code published at steveklebanoff/twitter_oauth_example.

If you see anywhere in which the code could be improved, please holler at me in the comments here, or in a Github issue or PR.

Other Resources and Acknowledgements

ueberauth/ueberauth_twitter is an awesome example of how to do authentication via Twitter using ueberauth. That being said, it doesn’t provide functionality to make authenticated requests on behalf of a user, and if you use parroty/extwitter you don’t necessarily need the ueberauth dependency.

scrogson/oauth2_example has great examples on how to do OAuth 2 Authentication via a variety of providers, but it does not support Twitter which uses OAuth 1.

Thanks to cjlarose for taking some time to discuss the security aspects of storing OAuth tokens in cookies.

Setting up Ecto 2 in Test Environment

I’m currently working on writing a package that uses Ecto 2, but not phoenix. I needed to have my test database work the way Rails does: isolating testing to a specific test database, and ensuring the test database is clean after every run.

I tried modifying some Ecto 1 test_helper examples but was getting weird errors like this:

** (exit) exited in: GenServer.call(Ecto.Migration.Supervisor, {:start_child, [#PID<0.144.0>, TestRepo, :forward, :up, false]}, :infinity)
    ** (EXIT) no process
           (elixir) lib/gen_server.ex:564: GenServer.call/3
                    lib/ecto/migration/runner.ex:22: Ecto.Migration.Runner.run/6
                    lib/ecto/migrator.ex:121: Ecto.Migrator.attempt/6
                    lib/ecto/migrator.ex:71: anonymous fn/4 in Ecto.Migrator.do_up/4
                    lib/ecto/adapters/sql.ex:472: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3
    (db_connection) lib/db_connection.ex:973: DBConnection.transaction_run/4
    (db_connection) lib/db_connection.ex:897: DBConnection.run_begin/3
    (db_connection) lib/db_connection.ex:671: DBConnection.transaction/3

I then realized that Ecto 2 treats things a bit differently than Ecto 1. The following outlines the steps I took to setup a test database using Postgres in Ecto 2.

First, we need to be able to specify special configuration values just for our test environment. If you don’t have this already, put the following at the bottom of config/config.exs.

if Mix.env == :test do
  import_config "test.exs"
end

Now, we need to specify the connection details of our test database. We also need to tell the database to clear between test runs. Create the config/test.exs file with the following.

use Mix.Config
config :servant, TestRepo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "servant_test",
  hostname: "localhost",
  pool: Ecto.Adapters.SQL.Sandbox,
  priv: "test/support" # only needed if your project does not have specific migrations

config :servant, ecto_repos: [TestRepo]

See Making Sense of Ecto 2 SQL.Sandbox and Connection Ownership Modes for more information on Ecto.Adapters.SQL.Sandbox.

We now need to modify our mix.exs file to autoload the test/support files we will be using, as well as have it run create the test database and run migrations when running tests.

Modify your mix.exs file as follows:

  def project do
    [...
     elixirc_paths: elixirc_paths(Mix.env),
     ...
     aliases: aliases
    ]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  defp aliases do
    ["test": ["ecto.create --quiet", "ecto.migrate", "test"]]
  end

Now, I am going to setup a Test repo to interact with the test database. Create the file test/support/repo.ex and add the following:

defmodule TestRepo do
  use Ecto.Repo, otp_app: :servant
end

Now, since my elixir project doesn’t have any actual migrations in `lib`, I need to create a migration to be used in the testing environment. I added the following in test/support/migrations/migrations.exs:

NOTE If you have migrations in your actual project, you can alternatively specify a directory to real migrations in the priv value set in mix.exs at the beginning of this post.

defmodule TestMigration do
  use Ecto.Migration

  def change do
    ....
  end
end

We now have to modify our test/test_helper.exs to use the SQL Sandbox mode to rollback all changes. See:

ExUnit.start()

defmodule Servant.TestCase do
  use ExUnit.CaseTemplate

  setup do
    # Explicitly get a connection before each test
    # By default the test is wrapped in a transaction
    :ok = Ecto.Adapters.SQL.Sandbox.checkout(TestRepo)

    # The :shared mode allows a process to share
    # its connection with any other process automatically
    Ecto.Adapters.SQL.Sandbox.mode(TestRepo, { :shared, self() })
  end
end


{:ok, _pid} = TestRepo.start_link
Ecto.Adapters.SQL.Sandbox.mode(TestRepo, { :shared, self() })

Now, I just have to ensure that my tests are doing using

use Servant.TestCase

instead of

use ExUnit.Case

and we should be good to go.

Was this useful? Did this work for you? Let me know in the comments.

Auto-post to Twitter and Facebook from Rails in 10 minutes

My newest project, kind.fm, automatically creates awesome Spotify playlists from track data entered into Spinitron.

Recently, I wanted to add some social integration into kind.fm.  Whenever a new playlist was added, I wanted to auto-post the new playlist to Twitter and Facebook.

The Hard Way

I have done some social integrations with Rails before, so I started thinking about how to do this.  It went something like this:

– Register new Twitter developer app- Store Twitter developer app credentials in Rails
– Using omniauth, generate authorization URL with proper credentials to get authentication token for twitter
– Store authentication token credentials in Rails
– Integrate rails callback to use client API gem to tweet every time new playlist is added
– Rinse and repeat above steps for Facebook
– Ensure callback uses a background job so it doesn’t slow the rest of the app down, and can be retried.

If you don’t already have a background job system yet, the last step is significant, even with the introduction of ActiveJob. You still need to decide on an adapter (Sidekiq? DelayedJob? Resque?), and most adapters require adding additional technology to your stack (i.e. Redis).

The Fast Way (~10 minutes)

I wanted to get this done quickly, and found a creative way to get this going in about 10 minutes, as opposed to the few hours the ‘hard way’ would take.

You may be familiar with services like Zapier and IFTTT.  These services allow you to setup tasks based on actions, i.e. “Whenever I post an instagram photo, re-post on my blog”.  It turns out you can leverage these services to easily perform simple social integration, by setting up a RSS feed of the things you’d like posted to Twitter and Facebook.

Here’s what I did:

First, I created an “index” action for my playlists_controller that outputs a RSS feed of social messages for the last 30 playlists. Based on params[:social] (either ‘facebook’ or ‘twitter’), I decide what message to output for the RSS feed.

# app/controllers/playlists_controller.rb
class PlaylistsController < ApplicationController
  def index
    @playlists = Playlist.active.includes(:show).where(
      show_id: Show.active.pluck(:id)
    ).order('played_on DESC').limit(30)
  end
 
end
# app/views/playlists/index.rss.builder
xml.instruct! :xml, :version => '1.0'
xml.rss :version => '2.0' do
  xml.channel do
    xml.title 'kind.fm playlists'
    xml.description 'discover inspired music'
    xml.link 'http://kind.fm'
 
    @playlists.each do |playlist|
      xml.item do
        xml.title "#{playlist.show.name} | #{playlist.played_on_formatted}"
 
        # choose description based on social media platform
        # uses custom functions on models, they might be better
        # suited for a helper though
        description = if params[:social] == 'twitter'
          playlist.twitter_description
        else
          playlist.facebook_description
        end
        xml.description description
 
        xml.pubDate playlist.played_on.to_s(:rfc822)
        xml.link playlist_url(playlist)
        xml.guid "playlist-#{playlist.id}"
      end
    end
  end
end

Now, it’s time to integrate the RSS feed with Zapier. I chose Zapier over IFTTT because it was easier to test out my integration.  First, I linked kind.fm’s twitter account and facebook account with Zapier. Then I set up two tasks:

When a new item appears on RSS feed http://kind.fm/playlists.rss?social=twitter, post to kind.fm’s twitter account

When a new item appears on RSS feed http://kind.fm/playlists.rss?social=facebook, post to kind.fm’s facebook profile

That’s it.  When a new playlist is added, the RSS feed reflects this.  Zapier is polling the RSS feed every 15 minutes, and when a new item appears, it posts to Twitter and Facebook with a custom message.

Thoughts & Caveats

There are definitely some caveats to The Fast Way, using RSS feeds and Zapier:

– Zapier’s free edition only allows for 100 tasks per month (that’s task actions, so in my case, 100 new playlists).
– Zapier hits your Rails server every 15 minutes, adding additional load, but especially if you throw some simple caching on top of your RSS feed this should be very minimal.
– Zapier only checks the RSS feed every 15 minutes, so there will be a slight delay between a playlist being added and the social messages going through.
– Zapier branding. On Facebook, there will some text saying “Posted via Zapier” on each post, but it is pretty small.  Also, on twitter, your links will be shortened with the zapier shortener, so they will look like zpr.io/s7LZ

There are benefits to the “The Hard Way”, and you’re also setting yourself up for integration with users to sign up using Facebook or Twitter.  But, if you are looking for a quick and easy solution, and you don’t mind the caveats, posting to Facebook and Twitter using Zapier may be a great way to go.

Know of any other good ways to integrate social posting into Rails? Let me know in the comments.

Shameless plug: If use Spotify and are looking for some good music to code to, check out No Jay’s House Of Watts on kind.fm

Simple invitation access code using Rails 4 & Devise

Want a user to provide a simple access/invitation code to register for an account using Rails 4 and devise?

See my github gist below. Supports having one dumb access code, nothing fancy. But, worth sharing: it’s not immediately straight forward how to do this.

This is achieved by:

– Setting up a virtual attribute on the user model called invitation_code

– Telling Rails strong parameters to allow invitation_code as an attribute on user when signing up

– Adds form field for invitation_code on the registration page

Opposite of chomp in Ruby

There does not seem to be an existing function which does the opposite of ‘chomp’ in Ruby in the standard library.

If you’d like to use a function to remove a specific substring from the beginning of a string, try this core extension.  Note: This doesn’t do any magic with whitespace as chomp does.

class String
def remove_from_beginning(string_to_remove)
len = string_to_remove.size
if self[0..(len-1)] == string_to_remove
return self[len..-1]
end
self
end
end

Facebook signed request losing session in Rails

Is the session in your Rails app getting messed up after a user goes to a facebook tab app  on your server and receives a signed request?

This can be happening because the facebook app doesn’t send a csrf token in it’s token. You can fix this by adding removing the protect_from_forgery callback on the facebook app’s action. See http://stackoverflow.com/questions/1177863/how-do-i-ignore-the-authenticity-token-for-specific-actions-in-rails

PgAdmin III fe_sendauth: no password supplied error

If you’ve set up a local unix postgres database that does not have a password, you may be able to connect via the console and your app but run into an error when connecting through PgAdmin3.

If you’ve got your pg_hba.conf correct and are still experiencing issues: try leaving the host field blank in the connection as opposed to using “localhost” or “127.0.0.1”. This tells PgAdmin to connect via the local unix socket instead of TCP.

CakePHP saving many objects with HABTM associations using saveAll

Saving many objects with their HABTM relationships using CakePHP’s saveAll is not as straight forward as one may think.

After playing around with results from $this->data, I’ve found the format that works.

In the example below, I’m saving multiple Users who have and belong to many Categories (HABTM). John belongs to Category’s 1 and 3, and Matthew belongs to categories 5 and 6.

Below is the array structure I passed to saveAll to get the save working.

Array
(
    [0] => Array
        (
            [User] => Array
                (
                    [first_name] => John
                    [last_name] => Smith
                    [email] => john@aol.com
                    [password] => fakepassword
                )
 
            [Category] => Array
                (
                    [Category] => Array
                        (
                            [0] => 1
                            [1] => 3
                        )
 
                )
 
        )
 
    [1] => Array
        (
            [User] => Array
                (
                    [first_name] => Matthew
                    [last_name] => Johnson
                    [email] => matt@aol.com
                    [password] => fakepassword
                )
 
            [Category] => Array
                (
                    [Category] => Array
                        (
                            [0] => 5
                            [1] => 6
                        )
 
                )
 
        )
 
)

Know of a better, more logical way? Please let me know in this comments. This structure doesn’t feel natural to me at all.