File: parser.rb

package info (click to toggle)
libgettext-activerecord-ruby 2.1.0-3
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 608 kB
  • ctags: 285
  • sloc: ruby: 2,895; makefile: 13
file content (208 lines) | stat: -rw-r--r-- 7,167 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
=begin
  lib/gettext_activerecord/parser.rb - parser for ActiveRecord

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

=end

require 'gettext'
require 'gettext/tools/rgettext'
require 'gettext/tools/parser/ruby'

include GetText

ActiveRecord::Base.instance_eval do
  alias inherited_without_log inherited

  def inherited(subclass)
    puts "registering an ActiveRecord model for later processing: #{subclass}" if $DEBUG
    active_record_classes_list << "#{subclass}" unless subclass.name.empty?
    inherited_without_log(subclass)
  end

  def active_record_classes_list
    $active_record_classes_list ||= []
  end

  def reset_active_record_classes_list
    $active_record_classes_list = []
  end
end

module GetText
  module ActiveRecordParser
    extend GetText
    include GetText
    bindtextdomain "gettext_activerecord"

    @config = {
      :db_yml => "config/database.yml",
      :db_mode => "development",
      :activerecord_classes => ["ActiveRecord::Base"],
      :untranslate_classes => ["ActiveRecord::Base", "ActiveRecord::SessionStore::Session"],
      :untranslate_columns => ["id"],
      :untranslate_table_name => false,
      :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"]
    #   * :untranslate_classes - an Array of the modules/class names which is ignored as the msgid.
    #   * :untranslate_columns - an Array of the column names which is ignored as the msgid.
    #   * :untranslate_table_name - a Boolean that avoids table name to be translated if it is true ... Generally, we don't have to translate table_name, do we? Maybe it is not true..... but it is a test
    #   * :adapter - the options for ActiveRecord::Base.establish_connection. If this value is set, :db_yml option is ignored.
    #   * :host - ditto
    #   * :username - ditto
    #   * :password - ditto
    #   * :database - ditto
    #   * :socket - ditto
    #   * :encoding - ditto
    #
    # "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)
      puts "\nconfig: #{config.inspect}\n\n" if $DEBUG
      if config
        config.each{|k, v|
          @config[k] = v
        }
      end
      @ar_re = /class.*(#{@config[:activerecord_classes].join("|")})/
    end

    def translatable_class?(klass)
      if klass.is_a?(Class) && klass < ActiveRecord::Base
        if klass.untranslate_all? || klass.abstract_class? || @config[:untranslate_classes].include?(klass.name)
          false
        else
          true
        end
      else
        true
      end
    end

    def translatable_column?(klass, columnname)
      ! (klass.untranslate?(columnname) || @config[:untranslate_columns].include?(columnname))
    end

    def parse(file, targets = []) # :nodoc:
      puts "parse file #{file}" if $DEBUG
      
      GetText.locale = "en"
      old_constants = Object.constants
      begin
        eval(open(file).read, TOPLEVEL_BINDING)
      rescue
        $stderr.puts _("Ignored '%{file}'. Solve dependencies first.") % {:file => file}
        $stderr.puts $! 
      end
      #loaded_constants = Object.constants - old_constants
      loaded_constants = ActiveRecord::Base.active_record_classes_list
      ActiveRecord::Base.reset_active_record_classes_list
      loaded_constants.each do |classname|
        klass = eval(classname, TOPLEVEL_BINDING)
        if translatable_class?(klass)
          puts "processing class #{klass.name}" if $DEBUG 
          add_target(targets, file, ActiveSupport::Inflector.singularize(klass.table_name.gsub(/_/, " "))) unless @config[:untranslate_table_name]
          unless klass.class_name == classname
            add_target(targets, file, ActiveSupport::Inflector.singularize(klass.to_s_with_gettext.gsub(/_/, " ").downcase))
          end
          begin
            klass.columns.each do |column|
              if translatable_column?(klass, column.name)
                if @config[:use_classname]
                  msgid = klass.to_s_with_gettext + "|" +  klass.human_attribute_name(column.name)
                  else
                  msgid = klass.human_attribute_name(column.name)
                end
                add_target(targets, file, msgid)
              end
            end
          rescue
            $stderr.puts _("No database is available.")
            $stderr.puts $!
          end
        end
      end
      if RubyParser.target?(file)
        targets += RubyParser.parse(file)
      end
      targets
    end

    def add_target(targets, file, msgid) # :nodoc:
      po = PoMessage.new(:normal)
      po.msgid = msgid
      po.sources << "#{file}:-"
      targets << po
      targets
    end

    def target?(file) # :nodoc:
      init(nil) unless @ar_re
      data = IO.readlines(file)
      data.each do |v|
        if @ar_re =~ v
          unless ActiveRecord::Base.connected?
            begin
              require 'rubygems'
            rescue LoadError
              $stderr.puts _("rubygems are not found.") if $DEBUG
            end
            begin
              ENV["RAILS_ENV"] = @config[:db_mode]
              require 'config/boot.rb'
              require 'config/environment.rb'
              require_rails 'activesupport'
              require_rails 'gettext_activerecord'
            rescue LoadError
              require_rails 'rubygems'
              gem 'activerecord'
              require_rails 'activesupport'
              require_rails 'active_record'
              require_rails 'gettext_activerecord'
            end
            begin
              yaml = YAML.load(IO.read(@config[:db_yml]))
              if yaml[@config[:db_mode]]
                ActiveRecord::Base.establish_connection(yaml[@config[:db_mode]])
              else
                ActiveRecord::Base.establish_connection(yaml)
              end
            rescue
              if @config[:adapter]
                ActiveRecord::Base.establish_connection(@config)
              else
                return false
              end
            end
          end
          return true
        end
      end
      false
    end
  end
  
  RGetText.add_parser(GetText::ActiveRecordParser)
end