Flattening Array Containing Hashes in Ruby

We ran into an issue in one of our tests in which we needed to take data that was returned in the format of an array of hashes (which could contain nested arrays of hashes), and convert it into a flat hash (no nesting). Here’s a example for us visual people:

[
  { title: 'title' },
  { name: 'name' },
  details: [
    { item1: '1' },
    { item2: '2' }
  ]
]

This needed to be converted to this:

{
  title: 'title',
  name: 'name',
  item1: '1',
  item2: '2'
}

We didn’t have to worry about overlapping keys because we knew beforehand that there would never be duplicate keys in our original data. Here’s the solution I came up with.

def array_to_hash(array)
  array.inject({}) do |memo, hash|
    hash.each do |k,v|
      if v.is_a?(Array)
        memo.merge!(array_to_hash(v))
      else
        memo[k] = v
      end
    end

    memo
  end
end

This handles any level of nesting that you may have. I personally thought it was a simple solution :-).

Abstracting Away Gem Dependencies In Rails

As I have been working with Rails for the past few years there is one thing that has constantly bothered me on most projects I work on. The problem is the hard dependencies that Rails applications have on gems. Generally, when I have wanted to connect my application with Twitter I would:

  1. Go to GitHub and find a Gem
  2. Add it to my Gemfile and run bundle
  3. Start referencing the gem directly in my code

Number 3 is where the problem lies. If I am calling this gem directly, then I have a hard dependency upon it. If there are breaking changes in future releases (e.g. api changes) or if we want to use a different gem then we have to find all the places where we referenced this gem and make the necessary changes. In .NET (C#) you can easily abstract away dependencies by creating an interface and relying upon that interface in your calling code rather than a concrete class. In this way, your calling code is unaware of what your implementation is, all it knows is that as long as the class implements the interface it can call certain methods and properties on an object. But how can we deal with this problem in Ruby?

I think that the simplest way is to wrap gems in our own libraries or classes. Say for instance, that we have a Rails application called MyRailsApp and we want to use TwitterGemX in our project. Rather than reference TwitterGemX directly we can create our own wrapper class that will implement the interface we need for our project.

class MyRailsApp::TwitterClient
  def initialize(config = {})
    @inner_client = TwitterGemX.new(config)
  end

  def tweet(message)
    @inner_client.update(message)
  end
end

In this way any custom functionality you might need to add (e.g. logging) can be done in your own class rather than monkey-patching or forking a particular gem. I find this a better route to go than to reference gems directly. Not only this, but your calling code is completely unaware of what specific gem you are using. If Twitter updates their api and you find that the gem you are using is no longer compliant with that api, there is only one place that you need to go to make changes. If you want to switch to a different gem, you can do that as well. All your calling code knows is about MyRailsApp::TwitterClient :-).

Facebook Share And/Or Twitter Share Url Parameter Doesn’t Work

If you are trying to test out Twitter and/or Facebook sharing on your local development environment there is one thing you should be aware of. If you are passing a url parameter and that url parameter contains the “localhost” as your host then Facebook and Twitter will ignore these parameters. I experienced this recently while working on a Rails app.

Twitter

https://www.twitter.com/share

I would pass both tweet text and url parameter but only the tweet text would show up. This was caused by having localhost as my host. So in your staging or production environment these will work fine. If you want to just quickly verify then either in your tests or in your app add a :host option to your url helper and you can see that it works fine.

Facebook

https://www.facebook.com/sharer/share.php

I don’t know if this endpoint is officially deprecated or not but you can use Facebook Dialog instead.

With Facebook I would pass title, url, description but nothing would show up. Again this had to do with the fact that the url was using localhost as its host. Once I added a :host option to the url helper it worked fine. Just remember to remove the host option once you verify that your link to Facebook actually works properly.

ActiveRecord#save returns false but there are no errors

This is probably caused by a callback returning false. Any callbacks in the ActiveRecord callback chain that return false will halt the transaction and roll it back. You must make sure your callbacks always return a value that is not false.

ActiveRecord::Callbacks

If a before_* callback returns false, all the later callbacks and the associated action are cancelled. If an after_* callback returns false, all the later callbacks are cancelled.

ActiveModel::Callbacks

Like the Active Record methods, the callback chain is aborted as soon as one of the methods in the chain returns false.