making flickr's "blog this" work with mephisto

marcus • September 7th, 2007

this article refers to mephisto edge release 2983

i noticed this afternoon that i wasn’t able to configure my new flickr account to accept this blog for its “blog this” feature. digging down into mephisto’s logs, i was able to get it working by adding only a couple of things in vendors/plugins/mephisto_xmlrpc ...

since i have no clue how to create a proper patch-file, i wrote a little step by step guide detailing the changes:

in backend_controller.rb add a new web-service description (because flickr identifies itself as blogger)

web_service(:blogger)    { MetaWeblogService.new(self)  }

in meta_weblog_api.rb add the missing getUsersBlogs method definition:

# get a list of blogs that a user can access
api_method :getUsersBlogs,
   :expects => [ {:appkey => :string}, {:username => :string}, {:password => :string} ],
   :returns => [[MetaWeblogStructs::Blog]]

create a new file struct /vender/plugins/mephisto_xmlrpc/meta_weblog_structs/blog.rb

module MetaWeblogStructs
  class Blog < ActionWebService::Struct
    member :blogName,   :string
    member :blogid,     :string
    member :url,        :string
    member :isAdmin,    :boolean
  end
end

and finally don’t forget to add the actual method implementation in meta_weblog_service.rb

  def getUsersBlogs(appkey, username, password)   
    user = User.find_by_login(username)
    return if user.nil?

    blogs = Array.new

    for site in user.sites
      isAdmin = false
      for membership in user.memberships
        isAdmin = true if membership.site === site    
      end

      blogs << MetaWeblogStructs::Blog.new(
        :blogName => site.title,
        :blogid => site.id,
        :url => "http://#{site.host}",
        :isAdmin => isAdmin
      )
    end

    return blogs
  end

You should be now able to add your mephisto blog as a “MetaWeblogAPI enabled Blog” to your flickr account and start posting!

eclipse europa

marcus • August 11th, 2007

this is probably a few days old news but anyway … the eclipse foundation’s europa packages are officially released. download here!

Eclipse Europa is the annual release of Eclipse projects. Like last year’s Callisto release, the Europa release is a coordinated release of different Eclipse project teams. This year, the annual release includes 21 projects. By releasing these projects at the same time, the goal is to eliminate uncertainty about version compatibility and make it easier to incorporate multiple projects into your environment.

i was pleasantly surprised to discover the new dynamic languages toolkit project that is included in the default java distribution. a good reason to have another go at my jruby experiments soon. (after some initial success with writing opengl processing sketches in ruby i gave up this idea since the lack of IDE integration was just too annoying…)

irb Tab-Completion and Syntax Coloring

marcus • May 27th, 2007

This little gem upgrades your irb with tab-completion and syntax coloring. Very useful! Install with:
sudo gem install -y wirble
Next, create or edit a file called .irbrc in your home folder (~/.irbrc), and make sure these lines are included there:
require 'rubygems'
require 'wirble'
Wirble.init
Wirble.colorize
You’re done – now play with irb!

mysql backup script 0.1

marcus • May 26th, 2007

here’s a way to quickly backup all databases from a mysql server.
can be easily automated with a cronjob.

download the source

#!/usr/bin/env ruby -w

require "mysql"
require "fileutils"
require "rubygems"
require_gem "actionmailer"

# --------------------------------------------------------------------------------
# mysql backup script
# platform: linux/ mac os x
# author: Marcus Wendt (www.marcuswendt.com)
# created: Jan 10, 2007
# version: 0.1
# --------------------------------------------------------------------------------
module MW
  module Utils
    class MySQLBackup
      # configuration
      @@name = 'backup-name'
      @@baseurl = 'https://www.your-host.com/backup_dir'

      @@db_server = 'localhost'
      @@db_user = 'XXXX'
      @@db_pass = 'XXX'

      # further options
      @@verbose = true
      @@verbose_level = 1 # set this to 0 to see all messages
      @@mysqLdump = '/usr/local/mysql/bin/mysqldump'
      @@file_opts = {}
      @@tar_opts = 'cjf'
      @@tar_suffix = 'tar.bz2'

      # do it!
      def initialize
        @@file_opts = {:verbose => @@verbose} if @@verbose_level == 0

        @time_start = Time.now
        nl
        line
        log "mysql backup script started #{format_time(@time_start)} ..."
        line

        # setup directories
        nl
        log "> checking directory structure"
        FileUtils.mkdir_p(archive_dir, @@file_opts)
        remove_tmp
        FileUtils.mkdir_p(tmp_dir, @@file_opts)

        # dump data
        nl
        log "> dumping databases"
        for db in databases
          log  "  #{db} ..."
          exec "    #{@@mysqLdump} --user=#{@@db_user} --password=#{@@db_pass} #{db} > #{tmp_dir}/#{db}.sql"
        end

        # compress data
        nl
        log "> creating archive"
        FileUtils.cd(tmp_dir, @@file_opts)
        exec "  tar #{@@tar_opts} #{filename} *.sql"
        FileUtils.mv("#{tmp_dir}/#{filename}", "#{archive_dir}/#{filename}", @@file_opts)

        # clean up
        nl
        log "> cleaning up"
        remove_tmp

        # show final message
        @time_end = Time.now
        duration = round(@time_end - @time_start,2)
        nl
        log "finished! (script executed in #{duration} seconds)"

        # send notifications
      end

      # name of the current archive file
      def filename
         "#{name}.#{@@tar_suffix}"
      end

      # name of the current backup
      def name
        "#{timestamp}_#{@@name}"
      end

      def wd
        Dir.getwd    
      end

      def format_time(time)
        time.strftime("%H:%M")    
      end

      def timestamp
        @timestamp = Time.now.strftime("%Y.%m.%d_%H_%M") if @timestamp.nil?
        return @timestamp
      end

      def archive_dir
        @archive_dir = "#{wd}/archives" if @archive_dir.nil?
        return @archive_dir
      end

      def tmp_dir
        "#{archive_dir}/tmp"
      end

      def remove_tmp
        FileUtils.rm_rf(tmp_dir, @@file_opts) if File.exists?(tmp_dir)
      end

      def databases
        database_list=[]
        db = MySQLHelper.new 
        db.connect(@@db_server, @@db_user, @@db_pass)
        dbr = db.query('SHOW DATABASES')
        while row = dbr.fetch_row
          database_list << row.to_s
        end
        dbr.free
        db.close
        return database_list
      end

      def exec(command)
        log(command.gsub(@@db_pass, '****').gsub(@@db_user, '****'), 0)
        return `#{command}`
      end

      def log(str, level=1)
        puts str if @@verbose && level >= @@verbose_level
      end

      def line
        log "----------------------------------------------------"
      end

      def nl 
        log ""
      end

      def round(numeric, precision)
        pow=10.0**precision.to_f
        (numeric*pow).round / pow
      end
    end

    # helper classes
    # ----------------------------------------------------------------------
    class MySQLHelper
      def connect(server, user, pass)
        begin
          @dbh = Mysql.real_connect(server, user, pass)
        rescue Mysql::Error => e
          puts "Error code: #{e.errno}"
          puts "Error message: #{e.error}"
          puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
        end
      end

      def query(sql)
        @dbr = @dbh.query(sql) if @dbh
      end

      def free
        @dbr.free if @dbr
      end

      def close
        @dbh.close if @dbh
      end
    end
  end
end

# go!
MW::Utils::MySQLBackup.new

Strip contact data from strings

marcus • May 26th, 2007

we had to find a way to prevent users from publishing their contact information for our documenta accommodation project. here are a few regexp's that make you life easier (it's not 100% waterproof, but prevents most forms of cheating)
patterns = [
  /([\w\._%-]+[\s]*)(@|at|\[at\]|\(at\))([\s]*[\w\.-]+[\s]*\.[\s]*[a-zA-Z]{2,4})/i,
  /([http\:\/\/]*)([\s]*)([www\.]*)([\s]*)([\w\.-]+)([\s]*)\.([\s]*)(de|at|ch|com|org|net)/i,
  /[\d]{2,}/
]

result = orig
for p in patterns
  result = result.gsub(p, '')
end
the full script, including test:
#!/usr/bin/env ruby -w

#
# INPUT
#
orig = 
"""test@web.de
test2 @ web.de
test3 @ web . de
test4 AT web.de
test5(aT)betrug.de
test6 (at) betrug.de
test7[At]betrug.de
test8 [at] betrug.de
http://www.betrug.de
www.betrug.de
betrug.de
www.betrug.com
http://www.betrug.com
www . betrug . com
www. betrug.de
01702138958
01602138958 
0171 2138958
0561 1234567
+49 171 2138958
00491712138958
171 213895
"""

#
# REGEXP PATTERNS 
#
patterns = [
  /([\w\._%-]+[\s]*)(@|at|\[at\]|\(at\))([\s]*[\w\.-]+[\s]*\.[\s]*[a-zA-Z]{2,4})/i,
  /([http\:\/\/]*)([\s]*)([www\.]*)([\s]*)([\w\.-]+)([\s]*)\.([\s]*)(de|at|ch|com|org|net)/i,
  /[\d]{2,}/
]

result = orig
for p in patterns
  result = result.gsub(p, '')
end


#
# TEST
#
spacer = ""; 50.times do  spacer << "-" end
  
puts spacer
puts "ORIGINAL"
puts spacer

i=0
for line in orig
  puts "#{i}: #{line}"
  i=i+1
end  

puts spacer

i=0
for line in result
  puts "#{i}: #{line}"
  i=i+1
end

Easy templating in ruby

marcus • May 26th, 2007

ruby templating the easy way:
#!/usr/bin/ruby -w

require 'erb'

module MW
  module Utils
    module Template
      
      ENDL = "\n"
      
      #
      # compiles the given template
      #
      def self.compile(template, locals={})
        # create code for easyaccess locals
        local_code = "<% " << ENDL
        for key in locals.keys
          local_code << "#{key} = locals[:#{key}]" << ENDL
        end
        local_code << "%>" << ENDL
        template = local_code << template
  
  puts template
  
        # compile template
        erb = ERB.new(template, 0, "%<>")
        compiled = erb.result(binding)
        compiled.gsub(/\n$/,'') # chomp the trailing newline
      end
    end
  end
end


#
# test 1
#
t = "hi <%= firstname %> <%= lastname %>"
puts MW::Utils::Template.compile(t, :firstname=>"peter", :lastname=>"meier")
puts MW::Utils::Template.compile(t, :firstname=>"marcus", :lastname=>"wendt")

#
# test 2
#
class TestUser
  attr_accessor :first, :last
end

u = TestUser.new
u.first = "peter"
u.last = "meier"

t = "hallo <%= user.first %> <%= user.last %>"
puts MW::Utils::Template.compile(t, :user=>u)

random pronounceable password generator

marcus • May 26th, 2007

a quick & simple random pronounceable password generator for ruby.

def random_pronouncable_password(size = 6)
  consonants = %w(b c d f g h j k l m n p qu r s t v w x z ch cr fr nd ng nk nt ph pr rd sh sl sp
st th tr 0 1 2 3 4 5 6 7 8 9 0)
  vocals = %w(a e i o u y)
  
  alternate=true
  password=''
  
  (size * 2).times do
    # get a random vocal or consonant
    chunk = (alternate ? consonants[rand * consonants.size] : vocals[rand * vocals.size])
    alternate = !alternate
    # randomly swap its case & add to password
    chunk = chunk.swapcase if rand > 0.5
    password << chunk
  end
  
  password
end
3d 4-space abstract aesthetic system aesthetics algorithm alien ambient ambisonics animation architecture art artificial audio audio research black&white book caskets classic clicks & cuts code color computer-vision conceptual art consoles cpp culture ddr design devices digtial fabrication documenta documentation drawing dynamics electricity electromagnetism electronics environment event exhibition experimental exploration fashion festival film flocking folk food fractal furniture gamedev generative genetic geometry glitch graphic hacks haptics hardware history hyperspace ideas illustration images inspiration installation instrument intelligence interactive interieur japan java knowledge management landscape library life light liquid live london math micro minimal modernism monochrome motion motion graphics multiples music naming nature nervous ink networked networking opensource osx painting paper particles performance personal photography physics playful politics press print processing processing.org programming quotes recipes research retro romance ruby scripts sculpture SENDUNG.net shopping snippets social software sound space space exploration craft space exploration craft orbiter supercollider swiss systems technology theory theremin toys transformed travel tricks typography universe video visual vj water web2.0 xcode