Wednesday, 25 September 2013

HTTP Basic authentication with Rails for iOS

Recently I've been getting into iOS and developing a interesting iOS app (more info coming soon!)

But, as the app is all about showing data of course I needed to create an API in my rails app to feed that data. But, I didn't want someone else to be able to grab it.

The Rails App Side

Configure a custom MIME type

First thing I did was set up a custom mime type in initializers/mime_types.rb:
Mime::Type.register_alias "text/json", :theapp
That let me call urls like /give_me_data.theapp

Add the 'respond_to'

In the applicable controllers add a respond_to (well, I already had one, so add to it):
respond_to :html, :xml, :theapp

Make templates with json_builder

I luuuvvvvv the json_builder gem. So I created a whack of templates with the .json_builder extension. Because of the mime type setting it would automatically get called.

Add the http_authentication stuff

I am a little bit lazy, so, I just chuck this into my application_controller. That means that all :theapp requests need authentication:

protected
def http_basic_authentication
    if request.format == :ios
        authenticate_or_request_with_http_basic do |username, password|
            username == 'myAmazingApp' && password == 'theBestestPasswordEver'
        end
    end
end

The iOS app side

The URLs

Simply chuck your authentication stuff into your urls:
NSString *const RegionDataBaseURL = @"http://myAmazingApp:theBestestPasswordEver@localhost:3000/regions/%@.theapp";

Pulling...Data

I do my stuff like this (thanks to Ray Wenderlich):
  dispatch_async(kBgQueue, ^{
    NSURL *url = [NSURL URLWithString: [NSString stringWithFormat:RegionDataBaseURL, [self.region permalink]]];
    
    NSData* data = [NSData dataWithContentsOfURL: url];
    [self performSelectorOnMainThread:@selector(fetchedRegionData:)
                           withObject:data waitUntilDone:YES];
  });
Yes, I could also put the username and password in some other place and sub it into the URL, but brute force works for now.

Like I say, I am starting out in iOS so maybe this is too noddy for all you pros, but, it works.

Tuesday, 24 September 2013

The gotcha's in Node.js - or how I reduced our bandwidth by 75%

So, I have this great application that distributes, in pretty close to real time, information across hundreds of screens in Germany. It does this with a combination of Rails, Node.js and HTML/Javascript.
But we noticed MASSIVE traffic.
My model was to pump out the data and have intelligent pages decide what to show.
It turns out this is very inefficient.

Not ready for primetime

We have 4 'channels' of information. The software had originally been built on a 'tv station' idea. And, in TV, everything gets pumped down the line (or over the air) and then the tv 'tunes in' to a channel and shows the appropriate content.
In our case, we had a 'screen structure' message that was sent out once a minute and then in between each element on the screen would send out its own updates if it was altered.
It was the screen structure message that was the problem.

The old way

I was doing something like this on the page:

client.subscribe('/source', function(message) {
    window.setTimeout(function() {
        process_source_message(message);
    }, 1);
})

And in Node app.js I was doing this:
app.post('/source_state', function(req, res) {
    bayeux.getClient().publish('/source', req.body);
    res.send(200);
});

And in Rails, something like this:
RestClient.post [NODE_CONFIG[Rails.env]['node_url'], NODE_CONFIG[Rails.env]['source_update_path']].join('/'), JSON(json)

So, every page would get every 'source' message.

The new way

I realised that I should have the pages listen to their specific channel. I had got hung up in the past on the idea that I didn't know the name of the source in advance - it could be anything as it is user defined.

But, inside of the source JSON was already this:
json = {
    "source" => {"name" => source.slug}
}

It turns out I didn't need to change the back end (Rails) at all.

In the app.js I just needed
app.post('/source_state', function(req, res) {
    bayeux.getClient().publish('/'+req.body.source.name, req.body);
    res.send(200);
});



Sunday, 30 October 2011

installing sunport_rails on ubuntu production

We've added search functionality to Toygaroo with Solr via sunspot_rails.

If you are going to install it on Ubuntu then make sure you do:

sudo apt-get install openjdk-6-jdk

First!

Saturday, 1 October 2011

Using the html5 boilerplate and rails 3

I am experimenting with using the html5 boilerplate with rails 3.0x. It is an interesting setup. I am using Russ Frisch's html5 boilerplate template, but updating it to use the latest boilerplate code.
It seems to be a pretty nice way of getting your css and js ducks in a row. Over the next few days I need to see how it handles having jquery mobile in the mix. I am also not 100% sure how to use a some sort of grid layout template (like 960 or 114opx) with it. Or even if you should!
We shall see.

Wednesday, 28 September 2011

Suffering Rails3 slowdown

We've just pushed our Rails 3 upgraded app to production... and are suffering a massive slowdown in insert/update speed over Rails 2.
At the moment I am not sure of the exact cause of this.

It *might* be mysql inserts, though I can't quite see why that would be.

It *might* be because this new version we are using vestal_versions to track changes.

It might be because the moon is in the house of Mars for all I know!

I hate getting stung by unknowns. The speed on our test environment is tolerable, slightly slower than the rails 2 version, but I was willing to accept that because the new version is doing so much more.

Bench-marking is one thing.. but know why the bench marks are slower is the key!

Monday, 26 September 2011

Expiring fragments from daemons

We have an application that gets its data from a series of daemons that go out and read in data. This works great, except, we are caching pages. And I'd like to expire those pages based on an update.
It turns out that an Observer doesn't have access to expire_action or fragment. And a Sweeper is not called from data-only (i.e non-controller) updates! Buggers!

But there is a solution. You can call the sweeper directly from your importer:

MySweeper.instance.clean_up(model_instance)

This works, except I couldn't get it reliably to expire the actions. So, I used direct calls to Rails.cache.delete to do this.

Thinking about it, I guess I could then have just written an observer! As those do get called from controller-less updates.

Thursday, 21 July 2011

meta_search sort_link helper and associations

It took me a while to find this, so, for my own memory I am going to quickly write this up.

I have a view that shows a table of objects (a pretty standard index view). The only issue was I want to sort on one of the columns that actually has data coming not from the main object but an association.
What I discovered is a line in a posting here that says:

You can define your own custom sort scopes. Define scopes named “sort_by__asc” and “sort_by__desc” and sort_link @search, :name will work as you might expect.

So, I have an object of 'info' defined like this:
class Info < ActiveRecord::Base
belongs_to :region
scope :contains_string, lambda {|str| where(:name.matches % "%#{str}%")}
search_methods :contains_string

scope :active, lambda{where(:active => true)}
search_methods :active, :type => :boolean
end


What I found is that you can put this at the end:
scope :sort_by_region_name, lambda{joins(:region).order("regions.name asc")}
search_methods :sort_by_region_name


and then in my view I can do:

<table>
<thead>
<tr>
<td width="25%"><%= sort_link @info_search, :name, "Info" %></td>
<td width="25%"><%= sort_link @info_search, :region_name, "Region" %></td>
<td><%= sort_link @info_search, :active %></td>
</tr>
</thead>
...body info...
</table


And in my controller:
class Admin::InfosController < Admin::BaseController
def index
search = params[:search] || {"meta_sort" => "name.asc"}

@info_search = Info.search(search)
@infos = @linfo_search.paginate(:page => params[:page]||1, :per_page => 15) # load all matching records
end
end


And presto I have an index that
a) default to sorting by name (see the first line of the index method)
b) let's me sort on an associated value

Cool!