#!/usr/bin/ruby
=begin
  parser/activerecord.rb - parser for ActiveRecord

  Copyright (C) 2005, 2006  Masao Mutoh
 
  You may redistribute it and/or modify it under the same
  license terms as Ruby.

  $Id: activerecord.rb,v 1.9 2006/05/13 17:20:52 mutoh Exp $
=end

require 'gettext'
require 'gettext/parser/ruby'

include GetText

module GetText
  module ActiveRecordParser
    extend GetText
    include GetText
    GetText.bindtextdomain("rgettext")

    @config = {
      :db_yml => "config/database.yml",
      :db_mode => "development",
      :activerecord_classes => ["ActiveRecord::Base"],
      :use_classname => true,
    }

    @ar_re = nil

    module_function
    def require_rails(file) # :nodoc:
      begin
	require file
      rescue MissingSourceFile
	$stderr.puts _("'%{file}' is not found.") % {:file => file}
      end
    end

    # Sets some preferences to parse ActiveRecord files.
    #
    # * config: a Hash of the config. It can takes some values below:
    #   * :use_classname: If true, the msgids of ActiveRecord become "ClassName|FieldName" (e.g. "Article|Title"). Otherwise the ClassName is not used (e.g. "Title"). Default is true.
    #   * :db_yml: the path of database.yml. Default is "config/database.yml".
    #   * :db_mode: the mode of the database. Default is "development"
    #   * :activerecord_classes: an Array of the superclass of the models. The classes should be String value. Default is ["ActiveRecord::Base"]
    #
    # "ClassName|FieldName" uses GetText.sgettext. So you don't need to translate the left-side of "|". 
    # See <Documents for Translators for more details(http://www.yotabanana.com/hiki/ruby-gettext-translate.html)>.
    def init(config)
      if config
	config.each{|k, v|
	  @config[k] = v
	}
      end
      @ar_re = /class.*(#{@config[:activerecord_classes].join("|")})/
    end

    def parse(file, targets = []) # :nodoc:
      old_constants = constants
      begin
        eval(open(file).read)
      rescue
        $stderr.puts _("Ignored '%{file}'. Solve dependencies first.") % {:file => file}
        $stderr.puts $! 
      end
      loaded_constants = constants - old_constants
      loaded_constants.each do |classname|
	klass = eval(classname)
	if klass < ActiveRecord::Base
	  add_target(targets, file, ::Inflector.singularize(klass.table_name.gsub(/_/, " ")))
	  tablename = klass.class_name
	  begin
	    klass.columns.each do |column|
	      if @config[:use_classname]
		msgid = tablename + "|" +  klass.human_attribute_name(column.name)
	      else
		msgid = klass.human_attribute_name(column.name)
	      end
	      add_target(targets, file, msgid)
	    end
	  rescue
	    $stderr.puts _("No database is available.")
	    $stderr.puts $!
	  end
	end
      end
      if RubyParser.target?(file)
	targets = RubyParser.parse(file, targets)
      end
      targets.uniq!
      targets
    end

    def add_target(targets, file, msgid) # :nodoc:
      key_existed = targets.assoc(msgid)
      if key_existed 
	targets[targets.index(key_existed)] = key_existed << "#{file}:-"
      else
	targets << [msgid, "#{file}:-"]
      end
      targets
    end

    def target?(file) # :nodoc:
      init(nil) unless @ar_re
      data = IO.readlines(file)
      data.each do |v|
	if @ar_re =~ v
	  unless @db
	    begin
	      require 'rubygems'
	    rescue LoadError
	      $stderr.puts _("rubygems are not found.") if $DEBUG
	    end
	    require 'active_record'
	    begin
	      yml = YAML.load(ERB.new(IO.read(@config[:db_yml])).result)
	    rescue
	      return false
	    end
	    ENV["RAILS_ENV"] = @config[:db_mode]
	    require_rails 'config/boot.rb'
	    require_rails 'config/environment.rb'
	    require_rails 'app/controllers/application.rb'
	  end
	  return true
	end
      end
      false
    end
  end
end

if __FILE__ == $0
  # ex) ruby model1.rb model2.rb 
  ARGV.each do |file|
    if GetText::ActiveRecordParser.target?(file)
      p file
      p GetText::ActiveRecordParser.parse(file)
    end
  end
end