Feb 06

In rspec, I usually need to define a matcher to match arguments. I don’t want to create a match for every should_receive method. I just want to assert the arguments I care. I try to define a code block to assert my call arguments. I google it for a while and could not find an answer.

I want to do something like:


User.should_receive(:find).with(assert_that(lambda { |args
  options = args.pop
  options[:include].should == expected_include
  options[:limit].should == 100
}).and_return(:users)

After reading rspec source code, I finger out rspec should_receive argument allow code block. That’s easier than what I expect. The finial code is:


User.should_receive(:find).with do |*args|
  options = args.pop
  options[:include].should == expected_include
  options[:limit].should == 100
  true
end.and_return(:users)

remember to return true at the end if all assertions pass.

Dec 30

SEO has become a hot topic in the Internet.  I am working in my personal project which I’d like to have a friendly URL. I need to create a slug as my friend URL. But, the slug is case sensitive in my case. for example, word ‘fisher’ is different to name ‘Fisher’ in a dictionary. unfortunately, MySQL is case sensitive by default. So I need to change the setting for the slug field in my table.

I change my slug field in MySQL by adding the following code into my migration script:


execute %{ALTER TABLE TABLE_NAME MODIFY slug varchar(255) COLLATE utf8_bin NOT NULL}

That works fine in my production database which is MySQL. However, I am using sqlite3 in test which is case sensitive by default. So I need to modify the field only in MySQL database. The finial code I put in my migration script is following:


if ActiveRecord::Base.configurations[RAILS_ENV]['adapter'] == 'mysql'
execute %{ALTER TABLE words MODIFY slug varchar(255) COLLATE utf8_bin NOT NULL}
end

COLLATE options in MySQL:

  • utf8_bin: compare strings by the binary value of each character
  • utf8_general_ci: compare strings using general language rules, case insensitive
  • utf8_general_cs: compare strings using general language case sensitive
Nov 27

50 days ago, I’ve launched my personal website. I spend 2 months of my spare time to build this web application based on ruby on rails. There’re 5 tools available at the moment.

1. RSS Feed Icon Generator

2. Javascript Optimizer / Optimiser

3. Ajax Activity Indicator Generator

4. Css Optimizer / Optimiser

5. HTML and CSS Rounded Corner Button Generator

Jan 03

Sometime I want to use a ruby library and implement something in ruby code so I could use the cool ruby features. JRuby provide the capability to run ruby code in Java. Therefore I could implement Java interface in Ruby.

1. Create the Java Interface

public interface Animal
{
public String speak();

public String move();
}

2. Implement the interface in Ruby


class Animal
def move
"Move..."
end

def speak
"Speak..."
end
end

3. Create a factory to create the concrete class of the interface


import org.jruby.Ruby;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.javasupport.JavaEmbedUtils;

import java.lang.reflect.Method;
import java.io.InputStream;
import java.io.File;

public class RubyFactory
{
public static final String RUBY_src="ruby";

public static <T> T getBean(Class<T> type)
{
Ruby runtime = Ruby.getDefaultInstance();

try
{
runtime.evalScript(runtime.evalScript("File.open('" + RUBY_SRC + File.separator + type.getSimpleName().toLowerCase() + ".rb').read").toString());
runtime.evalScript(extendRubyScript(type));
}
catch (Exception e)
{
System.err.println(e.toString());
}

Object c = runtime.evalScript(type.getSimpleName() + ".new");
c = JavaEmbedUtils.rubyToJava(runtime, (IRubyObject) c, type);
return (T) c;
}

private static String extendRubyScript(Class type)
{
String rubyScript = "require 'java'\n" +
"class " + type.getSimpleName() + "\n" +
"  include Java::" + type.getName().replace(".", "::") + "\n";

Method[] methods = type.getMethods();

for (Method method : methods)

{
String name = method.getName();
rubyScript += "  eval(\"alias " + name + " #{'" + name +        "'.gsub(/([A-Z]+)([A-Z][a-z])/,'\\1_\\2').gsub(/([a-z\\d])([A-Z])/,'\\1_\\2').tr('-', '_').downcase}\")\n";
}

rubyScript += "end";
return rubyScript;
}    public static void main(String[] args)
{

Animal animal = RubyFactory.getBean(Animal.class);
System.out.println(animal.move());

System.out.println(animal.speak());
}
}

All the ruby files should be put into the ruby directory. I follow the naming convertion as following:

interface Greeting -> greeting.rb with class Greeting

method sayGoodbye in java -> method say_goodbye in ruby

4. use the factory above, we could easily to implement another java interface.


public interface Greeting
{
public String sayGoodbye();

public String sayHello();
}

5. The implementation of interface greeting.


class Greeting
def say_hello()
"Hello"
end

def say_goodbye()
"Goodbye"
end
end

6. Test Greeting.


public class GreetingTest extends TestCase
{
private Greeting greeting;

@Before
protected void setUp() throws Exception
{
greeting = RubyFactory.getBean(Greeting.class);
}

@Test
public void testSayHello()
{
assertEquals("Hello", greeting.sayHello());
}

@Test
public void testSayGoodbye()
{
assertEquals("Goodbye", greeting.sayGoodbye());
}
}

In order to run the application, you need to include asm-2.2.3.jar, asm-commons-2.2.3.jar, backport-util-concurrent.jar and jruby-complete-1.0.1.jar

download source code