6

I'm currently experimenting with creating my own gem in Ruby. The gem requires some static resources (say an icon in ICO format). Where do I put such resources within my gem directory tree and how to I access them from code?

Also, parts of my extension are native C code and I would like the C-parts to have access to the resources too.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
DeX3
  • 5,200
  • 6
  • 44
  • 68
  • Ok, I found a way to do it by using __FILE__ from within the ruby code, but this seems rather dirty to me... – DeX3 Jun 15 '12 at 14:01
  • Yeah, that's what I would have done. There's not really another good way :/ (at least, that I can think of; I could be wrong) – Jwosty Jun 15 '12 at 14:55
  • Do you want a way to cleanly access the gem's files that are outside of the `lib` directory, or are you looking for an object-oriented way to work with your resources? – Matheus Moreira Jun 15 '12 at 15:11

1 Answers1

9

You can put resources anywhere you want, except in the lib directory. Since it will be will be part of Ruby's load path, the only files that should be there are the ones that you want people to require.

For example, I usually store translated text in the i18n/ directory. For icons, I'd just put them in resources/icons/.

As for how to access these resources... I ran into this problem enough that I wrote a little gem just to avoid repetition.

Basically, I was doing this all the time:

def Your::Gem.root
  # Current file is /home/you/code/your/lib/your/gem.rb
  File.expand_path '../..', File.dirname(__FILE__)
end

Your::Gem.root
# => /home/you/code/your/

I wrapped this up into a nice DSL, added some additional convenience stuff and ended up with this:

class Your::Gem < Jewel::Gem
  root '../..'
end

root = Your::Gem.root
# => /home/you/code/your/

# No more joins!
path = root.resources.icons 'your.ico'
# => /home/you/code/your/resources/icons/your.ico

As for accessing your resources in C, path is just a Pathname. You can pass it to a C function as a string, open the file and just do what you need to do. You can even return an object to the Ruby world:

VALUE your_ico_new(VALUE klass, VALUE path) {
    char * ico_file = NULL;
    struct your_ico * ico = NULL;

    ico_file = StringValueCStr(path);
    ico = your_ico_load_from_file(ico_file); /* Implement this */
    return Data_Wrap_Struct(your_ico_class, your_ico_mark, your_ico_free, ico);
}

Now you can access it from Ruby:

ico = Your::Ico.new path
Matheus Moreira
  • 17,106
  • 3
  • 68
  • 107