Posted by
Moser on February 7, 2010
There is a important difference between the two block syntaxes in Ruby: Their precedence.
Consider following code:
def method1(*args)
puts "method1 got a block" if block_given?
end
def method2(*args)
puts "method2 got a block" if block_given?
end
method1 method2 do
end
method1 method2 {
}
You would expect both method calls to produce the same result, wouldn’t you?
Output:
method1 got a block
method2 got a block
Obviously the do-end block is passed to the first method in the expression while the {} block is passed to the method called directly before it.
Another example shows that the assignment does not count as a method call here:
#[...]
class Foo
def bar=(o)
puts "Foo#bar= got a block" if block_given?
end
end
foo = Foo.new
foo.bar = method1 method2 do
end
#method1 got a block
foo.bar = method1 method2 {
}
#method2 got a block
Posted by
Moser on January 10, 2009
Update: dm-optlock now on github
I released a new version of dm-optlock. It is NOT compatible with v 0.1.2’s code, because replaced the set_locking_column method by a method called add_locking_column. Your DB should be compatible.
class Model
include DataMapper::Resource
...
add_locking_column :my_version_col
end
It takes a options hash just like DataMapper’s property method does. I added it to enforce some options on the locking column.
Posted by
Moser on October 1, 2008
Update(2009-3-3): dm-optlock now on github
Update(2009-1-10): New version
I should be learning for my test on next tuesday, but coding helps keeping me in a good mood.
Here is a little gem I will need in a future project for which I’ve choosen merb + datamapper. Datamapper doesn’t support any row level locking.
My code checks if the record you want to save was changed since you loaded it. If it was changed it raises an exception (DataMapper::StaleObjectError). It works quite like ActiveRecord’s optimistic locking.
You need to define this column in your model you want to be locked:
property :lock_version, Integer, :default => 0
Anywhere in your code where you update your objects you should be prepared to handle the exception:
begin
if @obj.update_attributes({:dummy => 123})
#successful
else
#validation errors?
end
rescue DataMapper::StaleObjectError
#tell the user that he was too slow
#or even better: show him the conflicts and let him merge the changes
end
The gem can be downloaded here:
dm-optlock-0.1.0.gem
Please let me know what you think of it. I’ll try and put it on RubyForge, too.
(00:27) Little update:
I added a ’set_locking_column’ method.
dm-optlock-0.1.2.gem
Update:
dm-optlock is on Rubyforge.
Now you can get it like this:
Posted by
Moser on September 18, 2008
While playing around with stable sorting I implemented Mergesort.
class Array
def mergesort(&cmp)
if cmp == nil
cmp = lambda { |a, b| a <=> b }
end
if size <= 1
self.dup
else
halves = split.map{ |half|
half.mergesort(&cmp)
}
merge(*halves, &cmp)
end
end
protected
def split
n = (length / 2).floor - 1
[self[0..n], self[n+1..-1]]
end
def merge(first, second, &predicate)
result = []
until first.empty? || second.empty?
if predicate.call(first.first, second.first) <= 0
result << first.shift
else
result << second.shift
end
end
result.concat(first).concat(second)
end
end
It is not suitable for productive use as it’s quite slow. It’s about ten times slower than the stable_sort method from my earlier post.
A little benchmark sorting an array of 2,000,000 random integers:
user system total real
Original Sort 0.630000 0.010000 0.640000 ( 0.639860)
Stable Sort 12.780000 0.700000 13.480000 ( 13.477982)
Mergesort 109.170000 13.420000 122.590000 (122.617323)
Posted by
Moser on September 18, 2008
Ruby uses some kind of Quicksort to sort its arrays. Unfortunately Quicksort is not stable.
When you get sorted data from your database, which almost certainly sorts faster than anything in Ruby, you sometimes may want the data to be resorted in a stable way.
I found following snippet:
class Array
def stable_sort
n = 0
sort_by {|x| n+= 1; [x, n]}
end
end
I extended it to accept a block like the original sort method does:
class Array
def stable_sort
n = 0
c = lambda { |x| n+= 1; [x, n]}
if block_given?
sort { |a, b|
yield(c.call(a), c.call(b))
}
else
sort_by &c
end
end
end