I recently updated two of the Rails apps I'm working with to use Ruby 2.0
When running bundle install I came across this error:
Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.
/Users/Gavin/.rvm/rubies/ruby-2.0.0-p0/bin/ruby extconf.rb
creating Makefile
make
compiling binding_of_caller.c
binding_of_caller.c:4:10: fatal error: 'vm_core.h' file not found
#include "vm_core.h"
^
1 error generated.
make: *** [binding_of_caller.o] Error 1
Gem files will remain installed in /Users/Gavin/.rvm/gems/ruby-2.0.0-p0@MyApp/gems/binding_of_caller-0.6.8 for inspection.
Results logged to /Users/Gavin/.rvm/gems/ruby-2.0.0-p0@MyApp/gems/binding_of_caller-0.6.8/ext/binding_of_caller/gem_make.out
An error occurred while installing binding_of_caller (0.6.8), and Bundler cannot
continue.
Make sure that `gem install binding_of_caller -v '0.6.8'` succeeds before bundling.
The problem, as mentioned in this thread on Github, is that 0.7.1 is the minimum version of binding_of_caller that supports Ruby 2.0.
I wasn't sure which of my apps gems were dependent on binding_of_caller 0.6.8 so I ran bundle update.
I also updated Rails from version 3.2.11 to 3.2.12
Problem solved
So I've finally set aside some time to play with Ruby 2.0 and it's great fun!
Here's what I had to do to install Ruby 2 on my iMac (iOS 10.8.2) using RVM and Homebrew
First off, there were a few of the library dependencies I didn't have on my machine (It's a pretty new setup) so I had to install them:
$ brew install autoconf
$ brew install pkg-config
$ brew install libyaml
$ brew install readline
$ brew install libxml2
$ brew install libxslt
You may already have some of these installed, or there may be dependencies that you still require - don't worry, RVM will let you know when you run the next step:
$ rvm install ruby-2.0.0-p0
To use this version, simple type
... or include this in your .rvmrc file
You can now start to make use of Ruby 2's cool new features, including named parameters:
def foo(bar, name: "Foo bar")
puts "#{bar} with #{name}"
end
foo("sing", name: "Cher") # => "sing with Cher"
Using Ruby 2.0 With Rails
Ruby 2.0 seems to be fully backwards compatible so it should be safe to use with Rails 3 apps. We're already running it on two apps we're developing at the moment and haven't come across any problems at all!.
Here are some more of the new features in Ruby 2
In two of the projects I'm working on just now I'm using plist files to store data. To save me from having to duplicate work in both apps, I created a module to make light work of reading and writing to plist files.
http://github.com/KatanaCode/PListReadWrite/
Installation
When I'm creating Ruby modules to share across more than one app I prefer to install the modules as git submodules.
That way if I need to update/patch I only need to update once, not in every app!
# From your project root dir
git submodule add git@github.com:KatanaCode/PListReadWrite.git lib/plist_rw
Alternatively you can just copy/paste the file directly to your app.
Example Usage
Lets assume we have this plist file in our resources directory...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>jim</key>
<dict>
<key>name</key>
<string>Jim</string>
<key>email</key>
<string>jim@jimsemail.com</string>
</dict>
<key>jane</key>
<dict>
<key>name</key>
<string>Jane</string>
<key>email</key>
<string>jane@janesemail.com</string>
</dict>
</dict>
</plist>
... and we want to copy it over to our app's documents directory so we can update/edit the plist.
# Check if the plist exists in our documents dir
PListRW.exist?(:users, directory = :documentsDir) # => false
# Check if the plist exists in our main bundle
PListRW.exist?(:users, directory = :mainBundle) # => true
# Copy the plist file from the main bundle to the documents dir
PListRW.copyPlistFileFromBundle(:users)
# Fetch the object from the plist file
@users_hash = PListRW.plistObject(:users, Hash) # => A hash containing the User data
# Update the data
@users_hash[:jim] # => { name: "Jim", email: "jim@jimsemail.com" }
@users_hash[:jim][:name] = 'James'
# Store the data back in the plist
PListRW.updatePlistFileWithObject(:users, @users_hash)
# Check this worked OK
PListRW.plistObject(:users, Hash)[:jim][:name] # => "James"