ActiveRecord.touch_without_callbacks

When updating to 2.3.4 I noticed that touch is
no longer a simple ‘update :updated_at’, but a save!,
which caused some code to break
(e.g. touch`ing in after_save == loop)

Usage

User.first.touch_without_callbacks
User.touch_without_callbacks([User.first.id, User.last.id])

Install
Paste somewhere…

# Provide .touch as it was in 2.3.2, simply update the :updated_at field.
class ActiveRecord::Base
  def touch_without_callbacks
    now = Time.now.utc
    self.class.touch_without_callbacks(id, now)
    self.updated_at = now
  end

  def self.touch_without_callbacks(ids, time=Time.now.utc)
    update_all({:updated_at=>time}, :id=>ids)
  end
end

What are your git-stats?

Also available as Gist

Ever wondered how much who adds/removes, its time to find out πŸ˜€
(those are real stats, I just obfuscated the names πŸ˜‰ )

Results

Git scores (in LOC):
mr-add              :  +482273       -9466       
justu               :  +286250       -159905     
grosser             :  +152384       -323344     
another             :  +121257       -82116      
naames              :  +104577       -13591      
justfor             :  +68716        -72446      
example             :  +7795         -4987       
andeven             :  +5100         -1730       
morenow             :  +4225         -2764       
finish              :  +17           -19       

Update: After working 2.5 years on this project my final stats where: +861345 -1115685 = -254340 LOC πŸ˜€

Install
Copy init git_stats.rb and ruby git_stats.rb
(you can add the names of people who commit with different users into the ‘same’ array)

#!/usr/bin/env ruby
# please add enhancements to http://gist.github.com/234560
require 'optparse'
start = Time.now.to_i
sort_by = '+'
STDOUT.sync = true

same = [
  ['my name','my-name']
]

# parse options
options = {}
OptionParser.new do |opts|
  opts.banner = <<BANNER
Show git stats, options are:
BANNER
  opts.on("--limit [LIMIT]", Integer, "Limit"){|x| options[:limit] = x }
  opts.on("--max-lines [SIZE]", Integer, "Max lines per commit  ignore large commits"){|x| options[:max_lines] = x }
  opts.on("--help", "Show help"){ puts self; exit }
end.parse!

# parse commits
pipe = open('|git log --pretty=format:"A:%an" --shortstat --no-merges')
authors = ["unknown"]
stats = {}

count = 0
loop do
  count += 1
  break if options[:limit] and count/2 > options[:limit] # break if limit was reached (2 lines per commit)
  line = pipe.readline rescue break

  if line =~ /^A\:(.*?)$/
    authors = $1.strip
    found = same.detect{|a| a.include?(authors)}
    authors = found.first if found
    authors = authors.split('&').map(&:strip) # split up multiple people
    next
  end

  if line =~ /files changed, (\d+) insertions\(\+\), (\d+) deletions/
    add = $1.to_i
    remove = $2.to_i
    if options[:max_lines] and (add + remove) > options[:max_lines]
      print 'x'
    else
      authors.each do |author|
        stats[author] ||= Hash.new(0)
        stats[author]['+'] += add
        stats[author]['-'] += remove
      end
      print '.'
    end
  end
end

longest = stats.map{|a,d|a.length}.max
stats = stats.sort_by{|a,d| -d[sort_by] }
stats = stats.map do |author, data|
  "#{author.ljust(longest)}:  +#{data['+'].to_s.ljust(10)}   -#{data['-'].to_s.ljust(10)} "
end

puts "\nGit scores (in LOC):"
puts stats.join("\n")
puts "Took #{Time.now.to_i - start} seconds"

Hash.pass and Hash.block that also work with HashWithIndifferentAccess

A new version of the old pass/block hack, but this time they also work as expected on HashWithIndifferentAccess (params/session…)

When attr_protected or attr_accessible are just to complicated, a simple user.attributes = params[:user].pass(:name, :email) just works.
Or user.attributes = params[:user].block(:admin) for the opposite…

# lets through the keys in the argument
# >> {:one => 1, :two => 2, :three => 3}.pass(:one)
# => {:one=>1}
def pass(*selected_keys)
  tmp = self.dup
  dup.block(*selected_keys).keys.each{|k| tmp.delete k }
  tmp
end

# blocks the keys in the arguments
# >> {:one => 1, :two => 2, :three => 3}.block(:one)
# => {:two=>2, :three=>3}
def block(*blocked_keys)
  tmp = self.dup
  blocked_keys.each{|k| tmp.delete k }
  tmp
end

Solving Background Processing with a Single, Generic Background Job

Again I read an article about background processing that generates many different jobs which all do the same: call method a on b with parameter z. At the moment we are using one GenericJob to handle all those cases.

It serializes ActiveRecord objects to a string representation, so that they do not get submittet as instance(often too large or deserialisation problems), if you do not need this feature, :send_later could be a good option for starting with generic jobs.

Usage

GenericJob.publish UserMailer, :deliver_notification,
  :args=>[user, comment], :priority=>2

Install

class GenericJob < DelayedJobBase
  DEFAULT_JOB_PRIORITY = 5

  # GenericJob.publish( Product, :find, :args=>[:all, {:conditions=>"1 = 2"}], :priority=>3 )
  def self.publish(klass, method, options={})
    args = options[:args] || []
    args = GenericJob.serialize_ar(args)
    priority = options[:priority] || DEFAULT_JOB_PRIORITY
    Delayed::Job.enqueue self.new(:class_name => klass.to_s, :method_name => method, :args => args), priority
  end

  def perform
    klass = message[:class_name].constantize
    args = GenericJob.deserialize_ar(message[:args]||[])
    klass.send(message[:method_name], *args)
  end

  private

  def self.serialize_ar(args)
    args.map do |arg|
      if arg.is_a?(ActiveRecord::Base)
        "ActiveRecord:#{arg.class}:#{arg.id}"
      else
        arg
      end
    end
  end

  def self.deserialize_ar(args)
    args.map do |arg|
      if arg.to_s =~ /^ActiveRecord:(\w+):(\d+)$/
        $1.constantize.find($2)
      else
        arg
      end
    end
  end
end

Ubuntu Karmic Koala is here, time for a fresh install

Since my old Ubuntu lived a long live and saw numerous hacks, I chose to reinstall from scratch, here are the steps I took:

  1. install dotfiles
  2. Skype
  3. Multi-clipboard: sudo apt-get install glipper
  4. Application laucher: sudo apt-get install gnome-do + enable skype plugin
  5. Ruby enterprise
  6. Rubymine + Desktop icon+ Meta key for Rubymine
  7. Mysql: sudo apt-get install mysql-server mysql-client libmysql-ruby
  8. Apache:Β  sudo apt-get install apache2 apache2-mpm-prefork apache2-prefork-dev
  9. Passenger
  10. SSL for apache/passenger
  11. gem sources -a http://gems.github.com
  12. gems…
  13. Java: sudo apt-get install sun-java6-jre
  14. Git: sudo apt-get install git-core

No more hacks, everything works, very fast startup (~10s), faster graphics (for intel chips) πŸ˜€

My new desktop

My new desktop