Monday, May 29, 2006

Beautify Fixtures (Ruby on Rails)

After generating fixtures from my development database (you can read about it here) I decided to make them nice.

Up to now, for example, Permissions were labelled permission_001 and so on. And that is quite counter-intuitive. A good convention to label permissions would be controller_action. For example:

user_new: 
action: new 
id: 19 
controller: user 

The same applies to roles (you can label them with their name) and permissions_roles (you can build the name from their associated role and permission names). This is what my small script does. It is a rake task.
desc "Labels Roles, Permissions and PermissionsRoles after their value so that
the fixture becomes more readable. You can specify the DIR where fixtures we want to modify are.

require 'yaml' 
class Hash 
    def find_item(key, value, default=nil) 
        each do |k, v| 
            return k if (v[key] == value) || (v[key] == value.to_i) 
        end 
        default 
    end 
end 

task :beautify_fixtures => :environment do 
    dir = ENV['DIR'] || "#{RAILS_ROOT}/test/fixtures" 
    db   = ENV['DB']   || 'development' 
    File.cp "#{dir}/roles.yml",             "#{dir}/roles.bkp.yml" 
    File.cp "#{dir}/permissions.yml",       "#{dir}/permissions.bkp.yml" 
    File.cp "#{dir}/permissions_roles.yml", "#{dir}/permissions_roles.bkp.yml" 
    permissions = YAML.load_file("#{dir}/permissions.bkp.yml") 
    roles = YAML.load_file("#{dir}/roles.bkp.yml") 
    File.open("#{dir}/permissions.yml", 'w') do |file| 
        permissions.each do |k, v| 
            controller = v['controller'] 
            action = v['action'] 
            file.write "#{controller}_#{action}:n" 
            v.each {|key, value| file.write "  #{key}: #{value}n"} 
        end 
    end 
    File.open("#{dir}/roles.yml", 'w') do |file| 
        roles.each do |k, record| 
            file.write "#{record['name'].downcase}:n" 
            record.each {|key, value| file.write "  #{key}: #{value}n"} 
        end 
    end 

    permissions = YAML.load_file("#{dir}/permissions.yml") 
    roles = YAML.load_file("#{dir}/roles.yml") 
    prs = YAML.load_file("#{dir}/permissions_roles.bkp.yml") 
    
    File.open("#{dir}/permissions_roles.yml", 'w') do |file| 
        prs.each do |k, record| 
            permission = permissions.find_item('id', record['permission_id']) 
            role = roles.find_item('id', record['role_id']) 
            file.write "#{role}_#{permission}n" 
            record.each {|key, value| file.write "  #{key}: #{value}n"} 
        end 
    end 
end 

You can find a downloadable version here [BROKEN!]

Friday, May 26, 2006

Dump db to fixtures (generate fixtures from db)

This is a quite interesting thread on rails ml with the solution to my problem.
I wanted to generate fixtures automatically from my db. This is a rake action that does the job well. Just put it into lib/tasks/ and call it dump_fixtures.rake

desc 'Dump a database to yaml fixtures. Set environment variables DB
and DEST to specify the target database and destination path for the
fixtures. DB defaults to development and DEST defaults to RAILS_ROOT/
test/fixtures.'

task :dump_fixtures => :environment do
    path = ENV['DEST'] || "#{RAILS_ROOT}/test/fixtures"
    db   = ENV['DB']   || 'development'
    sql  = 'SELECT * FROM %s'

    ActiveRecord::Base.establish_connection(db)
    ActiveRecord::Base.connection.select_values('show tables').each do |table_name|
        i = '000'
        File.open("#{path}/#{table_name}.yml", 'wb') do |file|
            file.write ActiveRecord::Base.connection.select_all(sql %
table_name).inject({}) { |hash, record|
                hash["#{table_name}_#{i.succ!}"] = record
                hash
            }.to_yaml
        end
    end
end
# ActiveRecord::Base.connection.select_values('show tables')
# is mysql specific
# SQLite:  ActiveRecord::Base.connection.select_values('.table')
# Postgres
# table_names = ActiveRecord::Base.connection.select_values(<<-end_sql)
#    SELECT c.relname
#    FROM pg_class c
#      LEFT JOIN pg_roles r     ON r.oid = c.relowner
#      LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
#    WHERE c.relkind IN ('r','')
#      AND n.nspname IN ('myappschema', 'public')
#      AND pg_table_is_visible(c.oid)
# end_sql

Wednesday, May 17, 2006

Open in iTerm from Finder

Since I've been asked to do it, I wrote the "Open in iTerm" app, that does the very same thing than "Open in Terminal" (except that it uses iTerm instead of Terminal.app)

Actually there could be bugs, but at least on my system I suppose it's not my fault, but rather it is iTerm that crashes a lot (maybe the AppleScript part has not been tested extensively on intel macs).

If you experience problems, let me know.

The script part that accesses iTerm is

tell application "iTerm"

        activate

        set myTerm to (make new terminal)

        tell myTerm

                set mySession to (make new session at the end of sessions)

                tell mySession

                        exec command "/bin/bash"

                        write text "cd \""& myPath &"\""

                end tell

        end tell

end tell

I bundled both apps in the very same package, and you can download it here.

I upgraded this script too with Marco's help.

on run

        tell application "Finder" to try

                set myPath to quoted form of ¬

                        POSIX path of ((target of window 1) as string)

        on error

                set myPath to "~"

        end try

        

        tell application "iTerm"

                activate

                set myTerm to (make new terminal)

                tell myTerm

                        set mySession to (make new session at the end of sessions)

                        tell mySession

                                exec command "/bin/bash"

                                write text "cd "& myPath

                        end tell

                end tell

        end tell

end run

Definitive?

After some discussion on icm and the help of tv and Marco (thank you guys) we came to this

on run

tell app "Finder"

try

set myPath to quoted form of ¬

POSIX path of ((target of window 1) as string)

on error

set myPath to "~"

end

set esiste to ((count (get ¬

name of every application process ¬

whose creator type is "ITRM")) is not 0)

end

tell app "iTerm"

activate

if esiste then

set myTerm to (make new terminal)

else

set myTerm to the first terminal

end if

tell myTerm

make new session at the end of sessions

tell the last session

exec command "/bin/bash"

write text "cd "& myPath

end

end

end

end

Open in Terminal from Finder

This is a trivial AppleScript
tell application "Finder"
        set targ to target of Finder window 1
        set myPath to POSIX path of (targ as string)
end tell
tell application "Terminal" to do script "cd \""& myPath &"\""
Here you can see a couple of screenshots.
If you want to download the application (with pretty icon) that uses it, you can download it from here.
Edit: Thanks to Marco Balestra for showing me his own script (that does the same thing, but is much cleaner). Now I included his script instead of mine.
on run
        tell application "Finder" to try
                set myPath to quoted form of ¬
                        POSIX path of ((target of window 1) as string)
        on error
                set myPath to "~"
        end try
       
        tell application "Terminal"
                do script "cd "& myPath
                activate
        end tell
end run

Monday, May 1, 2006

Open with BBEdit

This is a tiny Automator Workflow that opens the current Safari document with BBEdit.
Previously I wrote an AppleScript that did the very same job, however the new document was marked as "new", thus when one tried to close it, BBEdit would complain and ask if we wanted to save
tell application "Safari"
        set cur to document 1
        set mySource to source of cur
        set myName to name of cur
end tell
tell application "BBEdit"
        set x to make new text document with properties ¬
                {contents:mySource, name:myName}
        activate
end tell
However if we use the automator action "New BBEdit Document", it offers the possibility to "Set unmodified" (that is to say the new document won't ask if it has to be saved before closing).
I created a two step Automator workflow. The first step is a slightly modified version of the first part of the script. It is a "Run AppleScript action" containing
on run {input, parameters}
        tell application "Safari"
                set cur to document 1
                set mySource to source of cur
                set myName to name of cur
        end tell
        return mySource
end run
The second step is the "New BBEdit Document" action.
You can download the workflow here. I sugest to put it in the script menu.