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:
importcpfrom'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'),//...
The use of .property() requires the use of Ember's prototype extensions, which can be disabled. Because of that, addons can't rely on .property() being available.
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;
}
}
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:
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 extensionsrequire'mkmf'pkgconfig=with_config('pkg-config')||'pkg-config'pkgconfig=find_executable(pkgconfig)orabort"Couldn't find your pkg-config binary"$LDFLAGS<<' '+`#{pkgconfig} --libs-only-L foo`.chomp$CFLAGS<<' '+`#{pkgconfig} --cflags foo`.chompfind_header('foo.h')orabort"Missing foo.h"find_library('foo','foo_create')orabort"Missing foo library"# Give it a nameextension_name='ruby_foo'# The destinationdir_config(extension_name)# Do the workcreate_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.
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:
moduleOurModuleclassFooendend
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
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:
VALUEruby_foo_initialize(VALUEself,VALUEfilename){// make sure 'filename' is a string; raises an exception if it's notCheck_Type(filename,T_STRING);// convert the Ruby string to a C-style stringconstchar*filename_cstr=StringValueCStr(filename);// get the foo_handle we initialized in 'new'foo_handlehandle;Data_Get_Struct(self,structfoo_tag,handle);foo_load(handle,filename);returnself;}voidInit_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:
Lastly, let's create a new method on our class that uses the foo_process function from our library.
voidruby_foo_process(VALUEself,VALUEname){// make sure 'name' is a string, and get a C-style string from the Ruby stringCheck_Type(name,T_STRING);constchar*name_str=StringValueCStr(name);// get our library handlefoo_handlehandle;Data_Get_Struct(self,structfoo_tag,handle);// call the library functionconstchar*value=foo_process(handle,name);returnrb_str_new2(value);}voidInit_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:
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.
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.