Phil Gengler


on software, development, and anything else

Introducing ember-property-computed

Today I released ember-property-computed, a small ember-cli addon to allow specifying computed properties by giving the function first and then the dependent keys.

Basically, in Ember there are two ways of specifying computed properties:

aliasPropertyName: function() {
  // do something
}.property('dependentKey')

and

aliasPropertyName: Ember.computed('dependentKey', function() {
 // do something
}

Notice that the two styles have the ordering of the two parts (the function and the list of dependent keys) switched; with .property() the function appears first, followed by the keys, while with Ember.computed the dependent keys are specified first.

What ember-property-computed does is provide a compromise between the two; it isn't a prototype extension so it's safe to use in addons*, but it preserves the function-then-keys ordering.

To use it, install the ember-property-computed addon in your project by running ember install ember-property-computed. Then, in any file where you want to define computed properties, import the function:

import cp from 'ember-property-computed';

You can replace cp with whatever name you want to use for the function; I chose cp because it's short and stands for "computed property".

Then, when you want to define a computed property, use the function like this:

//...
computedPropertyName: cp(function() {
  /* do whatever here */
}, 'dependentKeyName'),
//...

Configuring nginx for an Ember frontend and a Rails backend

One of the projects I'm working on at the moment is a todo list app, with an Ember.js frontend and a Rails API backend. Since it's something that only I'm going to be using (at least for the moment) I decided to keep things simple and just use basic HTTP authentication. With two pieces, I thought the easiest thing to do would be to host both parts on the same subdomain, so that I wouldn't have to deal with CORS and trying to forward the authentication.

I configured the routes for the Rails part to all start with api/v1, so that makes it easy to distinguish requests for the API from requests for the frontend. While I was at it, I also wanted to leave "real" URLs for the various routes in the Ember app, instead of using hashbang URLs.

upstream todo-api {
        server unix:/tmp/todo-api.unicorn.sock fail_timeout=0;
}

server {
        listen 80;
        server_name example.com; # replace with the actual server name

        root /srv/sites/todolist/www;
        access_log /srv/sites/todolist/logs/access.log;
        error_log /srv/sites/todolist/logs/error.log;

        auth_basic "Todolist";
        auth_basic_user_file "/srv/sites/todolist/htpasswd";

        location /api {
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header Host $http_host;
                proxy_redirect off;
                proxy_pass http://todo-api;
        }

        location / {
                try_files $uri $uri/ /index.html?/$request_uri;
        }
}

Wrapping a C API in Ruby

Here's the situation I found myself in earlier today: write a program that makes use of an API written in C, but do a bunch of additional setup & processing. The thought of writing all the code to open/read a file, connect to the database, and all that other stuff didn't really appeal to me. Since I only needed a couple of functions out of the API, this seemed like a great case for wrapping the API and writing the rest of the program in Ruby.

There's a lot out there about how to do this, but I didn't find any one place that put it all together neatly. So I decided to write up how I went about it in the hope that this may be useful to someone.

For this explanation let's say we want to wrap the (fictional) C library libfoo, which has these functions/types that we're interested in:

typedef struct foo_tag* foo_handle;
foo_handle foo_create(void);
void foo_load(foo_handle, const char* filename);
void foo_destroy(foo_handle);
const char *foo_process(foo_handle, const char* name);

The first step is to create a file called extconf.rb. This uses the "mkmf" gem and specifies the basic setup for our wrapper, like the name of it and where to find the headers and libraries we need.

# Loads mkmf which is used to make makefiles for Ruby extensions
require 'mkmf'

pkgconfig = with_config('pkg-config') || 'pkg-config'
pkgconfig = find_executable(pkgconfig) or abort "Couldn't find your pkg-config binary"

$LDFLAGS << ' ' + `#{pkgconfig} --libs-only-L foo`.chomp
$CFLAGS << ' ' + `#{pkgconfig} --cflags foo`.chomp

find_header('foo.h') or abort "Missing foo.h"
find_library('foo', 'foo_create') or abort "Missing foo library"

# Give it a name
extension_name = 'ruby_foo'

# The destination
dir_config(extension_name)

# Do the work
create_makefile(extension_name)

If we run ruby extconf.rb we should see output like this:

checking for foo.h... yes
checkout for foo_init... -lfoo
creating Makefile

We'll also see it created a new file called "Makefile". If we run "make" right now nothing will happen, since we haven't created any source files yet.

Because we used "ruby_foo" as the name for our extension in extconf.rb, let's create a new file called ruby_foo.c. This is where we'll define the interface between Ruby and the C library.

At its simplest, our file should look like this:

#include "ruby.h"

#include "foo.h"

VALUE our_module;
VALUE our_class;

void Init_ruby_foo()
{
    our_module = rb_define_module("OurModule");
    our_class = rb_define_class_under(our_module, "Foo", rb_cObject);
}

This defines a Ruby module called OurModule, containing a single Ruby class called Foo (we tell Ruby that this class inherits from Object). What we have so far is equivalent to:

module OurModule
   class Foo
   end
end

It's important that you name your function properly. Ruby expects the function to be called "Init_", where is the same name specified in extconf.rb.

So far, of course, our code doesn't do anything yet. Let's start adding some library calls.

Since our library has a foo_init function that returns a handle that we need to use for later calls, we'll need to make sure that we save this inside the Ruby object. To do this, we add an "allocator" function that calls foo_create and uses the Data_Wrap_Struct macro to

VALUE ruby_foo_new(VALUE klass)
{
   foo_handle handle = foo_create();
   return Data_Wrap_Struct(klass, 0, foo_destroy, handle);
}

void Init_ruby_foo()
{
    // ...
    rb_define_alloc_func(our_class, ruby_foo_new);
}

The middle two arguments of Data_Wrap_Struct are a "mark" function and a "free" function. The mark function is useful if the object you're creating contains one or more Ruby objects; since we're not in this example, we can pass 0 here. The "free" function is called when the Ruby garbage collector tears down an instance of our class. It's useful for freeing any memory that we allocated; in this case, we just use the library's foo_destroy function, though if we had more complicated logic we could create our own function and specify that here. Both the "mark" and "free" functions receive the pointer we're wrapping (the fourth argument to Data_Wrap_Struct).

Of course, at this point, our class only creates a handle to the library but doesn't finish the additional setup it needs (the foo_load function). This requires us to specify a 'filename' parameter, which we can do from Ruby. Let's define an initialize method for our new class:

VALUE ruby_foo_initialize(VALUE self, VALUE filename)
{
    // make sure 'filename' is a string; raises an exception if it's not
    Check_Type(filename, T_STRING); 

    // convert the Ruby string to a C-style string
    const char* filename_cstr = StringValueCStr(filename);

    // get the foo_handle we initialized in 'new'
    foo_handle handle;
    Data_Get_Struct(self, struct foo_tag, handle);

    foo_load(handle, filename);

    return self;
}

void Init_ruby_foo()
{
    // ...
    rb_define_method(our_class, "initialize", ruby_foo_initialize, 1);
} 

The last argument to rb_define_method is the number of parameters the method requires; in this case, we only need the filename.

Now we can create an instance of our new class like this:

require_relative './ruby_foo'
obj = OurModule::OurClass.new("/tmp/ourfile")

Lastly, let's create a new method on our class that uses the foo_process function from our library.

void ruby_foo_process(VALUE self, VALUE name)
{
    // make sure 'name' is a string, and get a C-style string from the Ruby string
    Check_Type(name, T_STRING);
    const char *name_str = StringValueCStr(name);

    // get our library handle
    foo_handle handle;
    Data_Get_Struct(self, struct foo_tag, handle);

    // call the library function
    const char *value = foo_process(handle, name);

    return rb_str_new2(value);
}

void Init_ruby_foo()
{
    // ...
    rb_define_method(our_class, "process", ruby_foo_process, 1);
}

This is very similar to what we did for the initialize method. The main differences are that we call foo_process instead of foo_load, and instead of returning self we use the rb_str_new2 function to convert the C-style string we get from the library into a Ruby string and return that from our method.

Now we can write our Ruby code to use our library:

require_relative './ruby_foo'

obj = OurModule::OurClass.new('/tmp/ourfile')
puts obj.process('bar')

For comparison, the equivalent C code would look like:

#include <stdio.h>
#include "foo.h"

int main()
{
    foo_handle handle = foo_create();
    foo_load(handle, "/tmp/ourfile");
    const char *value = foo_process("bar");
    printf("%s\n", value);
    return 0;
}

Now we have a wrapped C API! I left out error checking/exceptions in the interest of simplicity. In another post, I'll explore this, along with how you can use more of Ruby's built-in types (arrays, hashes, etc.) from inside of C code.


A fresh start

I've completely redone the pgengler.net site, replacing the ten-year-old software that was running the site before, and that broke at some point and never got fixed.

All the old content is still available through archive.pgengler.net and I've tried to preserve all the old URLs with redirects to the archive site.

I hope to update this more often, and with more technical-focused content, but I make no promises about keeping any sort of regular schedule.