Archive for the ‘Ruby’ tag
Rails: Populating a dropdown list using 'form_for'
Last week we were trying to make use of Rails' 'form_for' helper to populate a dropdown list with the values of a collection that we'd set to an instance variable in our controller.
My colleague pointed out that we'd need to use 'collection_select' in order to do this.
We want to put the values in the 'foos' collection onto the page. 'foos' is a hash which defines some display values and their corresponding values like so:
class FooController < ActionController::Base def index # @mainFoo defined with some value irrelevant to this example @foos = { "value1" => 1, "value2" => 2 } end end
The code we need to do this looks like this:
<%form_for @mainFoo, :html => { :name=> "ourForm", :id=> "ourForm" },:url => {:controller => "foo", :action => :bar} do | mainFoo |%>
<%= foo.collection_select :foo_id, {"Please select"=>""}.merge!(@foos), :last, :first, { :selected => "Please select" }, {:name => "foo", :id => "foo"} %>The method signature that we're passing those parameters to reads like this:
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
In this case we want the selected value to always be 'Please select' so we need to specify that in the 'options' hash. If 'selected' wasn't specified in the hash then the code would try to make the selected value @mainFoo.foo_id which in this case has no value anyway.
The other thing which I thought was quite neat is the way that you need to provided the 'value_method' and 'text_method' as parameters so that the dropdown list can be constructed with the appropriate labels and values.
In this case we have the display values as the keys in the hash and the values as the values in the hash so we can retrieve those entries from the collection by using the ':last' and ':first' methods.
We end up in the following method:
def options_from_collection_for_select(collection, value_method, text_method, selected = nil) options = collection.map do |element| [element.send(text_method), element.send(value_method)] end
This creates an array of arrays which is used later on.
In our case the values passing through this method would read like this:
collection = { "value1" => 1, "value2" => 2 } options = collection.map do |element| [element.send(text_method), element.send(value_method)] end
=> [["value1", 1], ["value2", 2]]
I was initially a bit confused about how we were able to call the 'collection_select' method on 'mainFoo' but a quick browse of the ActionPack code showed that 'mainFoo' actually represents a wrapper around that object rather than the object itself.
Ruby: 'method_missing' and slightly misled by RubyMine
Another library that we're using on my project is ActionMailer and before reading through the documentation I was confused for quite a while with respect to how it actually worked.
We have something similar to the following piece of code…
Emailer.deliver_some_email…which when you click its definition in RubyMine takes you to this class definition:
class Emailer < ActionMailer::Base def some_email recipients "some@email.com" from "some_other_email@whatever.com" # and so on end end
I initially thought that method was called 'deliver_some_mail' but having realised that it wasn't I was led to the 'magic' that is 'method_missing' on 'ActionMailer::Base' which is defined as follows:
module ActionMailer ... class Base def method_missing(method_symbol, *parameters) #:nodoc: if match = matches_dynamic_method?(method_symbol) case match[1] when 'create' then new(match[2], *parameters).mail when 'deliver' then new(match[2], *parameters).deliver! when 'new' then nil else super end else super end end end end
The 'matches_dynamic_method?' function allows us to extract 'some_email' from the 'method_symbol'. That value is then passed into the object's initializer method and is eventually called executing all the code inside that method.
def matches_dynamic_method?(method_name) #:nodoc: method_name = method_name.to_s /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name) end
Reading through the documentation, the author gives the following reasons for having separate 'create' and 'deliver' methods:
ApplicationMailer.create_signed_up("david@loudthinking.com") # => tmail object for testing
ApplicationMailer.deliver_signed_up("david@loudthinking.com") # sends the email
ApplicationMailer.new.signed_up("david@loudthinking.com") # won't work!In C# or Java I think we'd probably use another object to build up the message and then pass that to the 'Emailer' to deliver it so it's quite interesting that both these responsibilities are in the same class.
It also takes care of rendering templates and from what I can tell the trade off for having this much complexity in one class is that it makes it quite easy for the library's clients – we just have to extend 'ActionMailer::Base' and we have access to everything that we need.
Ruby: Accessing fields
I've spent a little time browsing through some of the libraries used by my project and one thing which I noticed in ActiveSupport is that fields don't seem to be accessed directly but rather are accessed through a method which effectively encapsulates them inside the object.
For example the following function is defined in 'inheritable_attributes.rb'
def write_inheritable_attribute(key, value) if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) @inheritable_attributes = {} end inheritable_attributes[key] = value end
def inheritable_attributes @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES end
EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
If we were using C# we'd have instantiated '@inheritable_attributes' at the field definition with 'EMPTY_INHERITABLE_ATTRIBUTES' like so…
public class SomeClass { private Dictionary<string, object> inheritableAttributes = new Dictionary<string, object>(); }
…but we can't do that in Ruby because we don't need to explicitly define all our fields, we just start using them.
I'm assuming this is quite a common pattern in Ruby and in a way it's quite neat because it restricts the number of direct field references which will make it easier to change the underlying implementation. Kerievsky's narrowed change refactoring suddenly becomes less necessary!
For that reason I wonder whether it would be a useful pattern in C#/Java or if it would be overkill.
Bundler: Don't forget to call 'source'
Brian, Tejas and I (well mainly them) have been working on an application to give badges to people based on their GitHub activity at the Yahoo Open Hack Day in Bangalore and we've been making use of Bundler to pull in our dependencies.
Our Gemfile was originally like this:
gem "sinatra", "1.0" gem "haml", "3.0.13" gem "activesupport", "3.0.0.beta4", :require => false gem "tzinfo", "0.3.22" gem "nokogiri", "1.4.2" ...
For quite a while we were wondering why 'bundle install' wasn't actually resolving anything at all before we RTFM and realised that we needed to call 'source' at the top so that bundler knows where to pull the dependencies from.
Changing the file to look like this solved that problem:
source "http://rubygems.org" gem "sinatra", "1.0" gem "haml", "3.0.13" gem "activesupport", "3.0.0.beta4", :require => false gem "tzinfo", "0.3.22" gem "nokogiri", "1.4.2" ...
bundler still seemed to have problems resolving 'nokogiri' whereby I was getting various error messages, eventually ending up with this one:
Installing nokogiri (1.4.2) from .gem files at /Users/mneedham/.rvm/gems/ruby-1.8.7-p299/cache with native extensions /Library/Ruby/Site/1.8/rubygems/installer.rb:482:in `build_extensions': ERROR: Failed to build gem native extension. (Gem::Installer::ExtensionBuildError) /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby extconf.rb checking for iconv.h... yes checking for libxml/parser.h... yes checking for libxslt/xslt.h... yes checking for libexslt/exslt.h... yes checking for gzopen() in -lz... no ----- zlib is missing. please visit http://nokogiri.org/tutorials/installing_nokogiri.html for help with installing dependencies.
I had to follow the instructions on their website to get that working which isn't ideal as it means not everything is being resolved through bundler.
I'm not sure if there's a way to get around having to do that so if anyone know a way let me know!
Installing Ruby 1.9.2 with RVM on Snow Leopard
Yesterday evening I decided to try and upgrade the Ruby installation on my Mac from 1.8.7 to 1.9.2 and went on the yak shaving mission which is doing just that.
RVM seems to be the way to install Ruby these days so I started off by installing that with the following command from the terminal:
bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )
That bit worked fine for me but there are further instructions on the RVM website if that doesn't work.
My colleague David Santoro pointed me to a post on ASCIIcasts detailing the various steps to follow to get Ruby installed.
Unfortunately my first attempt…
rvm install 1.9.2
…resulted in the following error in the log file:
yaml.h is missing. Please install libyaml. readline.c: In function ‘username_completion_proc_call’: readline.c:1292: error: ‘username_completion_function’ undeclared (first use in this function) readline.c:1292: error: (Each undeclared identifier is reported only once readline.c:1292: error: for each function it appears in.) make[1]: *** [readline.o] Error 1 make: *** [mkmain.sh] Error 1
I thought that perhaps 'libyaml' was the problem but Michael Guterl pointed me to a post on the RVM mailing list which suggests this was a red herring and that the real problem was 'readline'.
That led me back to a post on the RVM website which explains how to install 'readline' and then tell RVM to use that version of readline when installing Ruby.
I tried that and then ran the following command as suggested on Mark Turner's blog post:
rvm install 1.9.2-head -C --enable-shared,--with-readline-dir=/opt/local,--build=x86_64-apple-darwin10
That resulted in this error:
ld: in /usr/local/lib/libxml2.2.dylib, file was built for i386 which is not the architecture being linked (x86_64) collect2: ld returned 1 exit status make[1]: *** [../../.ext/x86_64-darwin10/tcltklib.bundle] Error 1 make: *** [mkmain.sh] Error 1
Michael pointed out that I needed to recompile 'libxml2.2′ to run on a 64 bit O/S as I'm running Snow Leopard.
I hadn't previously used the 'file' function which allows you to see which architecture a file has been compiled for.
e.g.
file /usr/local/lib/libxml2.2.dylib /usr/local/lib/libxml2.2.dylib: Mach-O dynamically linked shared library i386
I had to recompile 'libxml2.2′ to run on a 64 bit O/S which I did by downloading the distribution from the xmlsoft website and then running the following commands:
tar xzvf libxml2-2.7.3.tar.gz cd libxml2-2.7.3 ./configure --with-python=/System/Library/Frameworks/Python.framework/Versions/2.3/ make sudo make install
Re-running RVM Ruby installation command I then had this error instead:
tcltklib.c:9539: warning: implicit conversion shortens 64-bit value into a 32-bit value ld: in /usr/local/lib/libsqlite3.dylib, file was built for i386 which is not the architecture being linked (x86_64) collect2: ld returned 1 exit status make[1]: *** [../../.ext/x86_64-darwin10/tcltklib.bundle] Error 1 make: *** [mkmain.sh] Error 1
I downloaded the sqlite3 distribution and having untarred the file ran the following commands as detailed on this post:
CFLAGS='-arch i686 -arch x86_64' LDFLAGS='-arch i686 -arch x86_64' ./configure --disable-dependency-tracking make sudo make install
Next I needed to recompiled 'libxslt' which I downloaded from the xmlsoft website as well before untarring it and running the following:
./configure make sudo make install
Having done that I re-ran the RVM Ruby installation command:
rvm install 1.9.2-head -C --enable-shared,--with-readline-dir=/opt/local,--build=x86_64-apple-darwin10
And it finally worked!
The magnificent yak is borrowed under the Creative Commons Licence from LiminalMike's Flickr Stream.
Iron Ruby: 'unitialized constant…NameError'
I've been playing around a bit with Iron Ruby and cucumber following Rupak Ganguly's tutorial and I tried to change the .NET example provided in the 0.4.2 release of cucumber to call a class wrapping Castle's WindsorContainer.
The feature file now looks like this:
# 'MyAssembly.dll' is in the 'C:/Ruby/lib/ruby/gems/1.8/gems/cucumber-0.6.4/examples/cs' folder require 'MyAssembly' ... Before do @container = Our::Namespace::OurContainer.new.Container end
The class is defined roughly like this:
public class OurContainer : IContainerAccessor { private WindsorContainer container = new WindsorContainer(); public SwintonContainer() { container.RegisterControllers(Assembly.GetExecutingAssembly()).AddComponent<IFoo, Foo>(); // and so on } public IWindsorContainer Container { get { return container; } } }
When I tried to run the feature like so:
icucumber features
I kept getting the following error:
uninitialized constant Our::Namespace::OurContainer (NameError) C:/Ruby/lib/ruby/gems/1.8/gems/cucumber-0.6.4/examples/cs/features/step_definitons/calculator_steps.rb:13:in `Before'
I've come across a few posts where people described the same error and they all suggested that IronRuby was unable to find the class that I was trying to call in the code.
I decided to try calling another class from the assembly to see if that was the problem but that worked fine so there wasn't a problem with locating the class.
Somewhat by coincidence I was looking at the assembly again in Reflector and tried to look at the constructor of the 'OurContainer' class and was asked to give the location of the 'Castle.Windsor' assembly which it uses internally.
I didn't have that assembly or any of its dependencies in the 'C:/Ruby/lib/ruby/gems/1.8/gems/cucumber-0.6.4/examples/cs' folder but once I'd included those it all worked fine again!
rspec – Invalid character '\240′ in expression
We have been using rspec on my project for the unit testing of our Ruby code and while running one of the specs last week I ended up getting this somewhat en-cryptic error message:
Invalid character '\240' in expression ...
After convincing myself that this error wasn't actually possible it turned out that I had somehow entered an 'invisible to TextMate' character after one of the method definitions – on the editor it just looked like a space.
I'm not sure exactly what the character was but deleting it got rid of the error and got my test/spec running again.
Calling shell script from ruby script
Damana and I previously posted about our experiences with different Ruby LDAP solutions.
Having settled on Ruby-LDAP (although having read Ola and Steven's comments we will now look at ruby-net-ldap) we then needed to put together the setup, installation and teardown into a ruby script file.
A quick bit of Googling revealed that we could use the Kernel.exec method to do this.
For example, you could put the following in a ruby script file and it would execute and show you the current directory listing:
exec "ls"
The problem with using Kernel.exec, which we became aware of after reading Jay's post, is that we lose control of the current process – i.e. the script will exit after running 'exec' and won't process any other commands that follow it in the file.
Luckily for us there is another method called Kernel.system which allows us to execute a command in a sub shell, and therefore continue processing other commands that follow it.
We were able to use this method for making calls to the make script to install Ruby-LDAP:
@extconf = "ruby extconf.rb" system @extconf system "make" system "make install"
There is one more option we can use if we need to collect the results called %x[...]. We didn't need to collect the results so we have gone with 'Kernel.system' for the time being.
Jay covers the options in more detail on his post for those that need more information than I have presented.
Ruby: Ignore header line when parsing CSV file
As my Ruby journey continues one of the things I wanted to do today was parse a CSV file.
This article proved to be very useful for teaching the basics but it didn't say how to ignore the header line that the CSV file contained.
The CSV file I was parsing was similar to this:
name, surname, location Mark, Needham, Sydney David, Smith, London
I wanted to get the names of people originally to use them in my code. This was the first attempt:
1 2 3 4 5 6 7 8 9 10 | require 'csv' def parse_csv_file_for_names(path_to_csv) names = [] csv_contents = CSV.read(path_to_csv) csv_contents.each do |row| names << row[0] end return names end |
I then printed out the names to see what was going on:
1 2 3 4 | names = parse_csv_file_for_names( "csv_file.csv" ) names.each do |name| puts name end |
This is what was printed:
name Mark David
It turns out that the 'shift' method is what I was looking for to help me ignore the first line of the file. The new and improved method now looks like this:
1 2 3 4 5 6 7 8 9 10 11 | require 'csv' def parse_csv_file_for_names(path_to_csv) names = [] csv_contents = CSV.read(path_to_csv) csv_contents.shift csv_contents.each do |row| names << row[0] end return names end |
Not a particularly complicated thing to do in the end although I had been expecting to find a method on CSV that would allow me to ignore the header line automatically. As far as I could tell there isn't one!
Ruby: Unzipping a file using rubyzip
In the world of Ruby I've been working on a script which needs to unzip a file and then run an installer which is only available after unpacking it.
We've been using the rubyzip gem to do so but so far it hasn't felt intuitive to me coming from the Java/C# world.
ZipFile is the class we need to use and at first glance I had thought that it would be possible to just pass the zip file name to the 'extract' method and have it do all the work for me!
Turns out you actually need to open the zip file and then create the directory location for each file in the zip before extracting them all individually.
We eventually ended up with this little method:
1 2 3 4 5 6 7 8 9 10 11 12 | require 'rubygems' require 'zip/zip' def unzip_file (file, destination) Zip::ZipFile.open(file) { |zip_file| zip_file.each { |f| f_path=File.join(destination, f.name) FileUtils.mkdir_p(File.dirname(f_path)) zip_file.extract(f, f_path) unless File.exist?(f_path) } } end |
Which we can then call with the zip file and the destination where we want to unzip the file.
1 | unzip_file("my.zip", "marks_zip") |
Is there a better way to do this? It feels a bit clunky to me at the moment.
