svenfuchs-i18n-tools 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.textile +1 -0
- data/bin/i18n-keys +6 -0
- data/lib/ansi.rb +19 -0
- data/lib/i18n/keys.rb +51 -0
- data/lib/i18n/keys/commands.rb +53 -0
- data/lib/i18n/keys/formatter.rb +39 -0
- data/lib/i18n/keys/index.rb +209 -0
- data/lib/i18n/keys/occurence.rb +120 -0
- data/lib/i18n/parser/erb_parser.rb +54 -0
- data/lib/i18n/parser/ruby_parser.rb +93 -0
- data/test/all.rb +1 -0
- data/test/commands_test.rb +1 -0
- data/test/erb_parser_test.rb +14 -0
- data/test/fixtures/source_1.rb +14 -0
- data/test/fixtures/source_2.rb +1 -0
- data/test/fixtures/template.html.erb +19 -0
- data/test/index_test.rb +135 -0
- data/test/keys_test.rb +75 -0
- data/test/occurence_test.rb +130 -0
- data/test/ruby_parser_test.rb +54 -0
- data/test/test_helper.rb +14 -0
- metadata +82 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Sven Fuchs <svenfuchs@artweb-design.de>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
to turn off filename generation in zsh: unsetopt GLOB
|
data/bin/i18n-keys
ADDED
data/lib/ansi.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ansi
|
2
|
+
COLORS = { :red =>";31", :yellow => ";33", :green => ";32" }
|
3
|
+
STYLES = { :bold => ";1", :underline => ";4" }
|
4
|
+
|
5
|
+
def ansi_format(text, formats)
|
6
|
+
res = "\e[0"
|
7
|
+
if formats.is_a?(Array) || formats.is_a?(Hash)
|
8
|
+
COLORS.each { |k,v| res += v if formats.include?(k) }
|
9
|
+
STYLES.each { |k,v| res += v if formats.include?(k) }
|
10
|
+
elsif formats.is_a?(Symbol)
|
11
|
+
COLORS.each { |k,v| res += v if formats == k }
|
12
|
+
STYLES.each { |k,v| res += v if formats == k }
|
13
|
+
elsif formats.respond_to?(:to_sym)
|
14
|
+
COLORS.each { |k,v| res += v if formats.to_sym == k }
|
15
|
+
STYLES.each { |k,v| res += v if formats.to_sym == k }
|
16
|
+
end
|
17
|
+
res += "m" + text.to_s + "\e[0m"
|
18
|
+
end
|
19
|
+
end
|
data/lib/i18n/keys.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/parser/ruby_parser'
|
2
|
+
require File.dirname(__FILE__) + '/parser/erb_parser'
|
3
|
+
require File.dirname(__FILE__) + '/keys/index'
|
4
|
+
|
5
|
+
module I18n
|
6
|
+
module Keys
|
7
|
+
VERSION = '0.0.1'
|
8
|
+
|
9
|
+
@@root = '.'
|
10
|
+
@@verbose = true
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def verbose?
|
14
|
+
@@verbose
|
15
|
+
end
|
16
|
+
|
17
|
+
def verbose=(verbose)
|
18
|
+
@@verbose = !!verbose
|
19
|
+
end
|
20
|
+
|
21
|
+
def root
|
22
|
+
@@root
|
23
|
+
end
|
24
|
+
|
25
|
+
def root=(dir)
|
26
|
+
@@root = dir
|
27
|
+
end
|
28
|
+
|
29
|
+
def meta_dir
|
30
|
+
dir = root + '/.i18n'
|
31
|
+
FileUtils.mkdir(dir) unless File.exists?(dir)
|
32
|
+
dir
|
33
|
+
end
|
34
|
+
|
35
|
+
def config
|
36
|
+
@config ||= YAML.load_file(meta_dir + '/config.yml') rescue { 'indices' => {} }
|
37
|
+
end
|
38
|
+
|
39
|
+
def config=(config)
|
40
|
+
@config = config
|
41
|
+
end
|
42
|
+
|
43
|
+
def index(*args)
|
44
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
45
|
+
name = args.first || options.delete(:index)
|
46
|
+
index = Index.load_or_create_or_init(name, options)
|
47
|
+
index
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
program :version, I18n::Keys::VERSION
|
2
|
+
program :description, 'ruby i18n tools'
|
3
|
+
|
4
|
+
command :find do |c|
|
5
|
+
c.syntax = 'i18n find [key] --index --verbose'
|
6
|
+
c.summary = 'Find keys passed to I18n.t()'
|
7
|
+
c.example 'i18n find', 'i18n find foo bar --index'
|
8
|
+
c.option '--index', 'Use an index'
|
9
|
+
c.option '--verbose', 'Output information about index build'
|
10
|
+
c.when_called do |args, options|
|
11
|
+
I18n::Keys.verbose = options.verbose
|
12
|
+
index = I18n::Keys.index(:index => options.index)
|
13
|
+
index.each(*args.map { |arg| arg.dup }) { |occurence| puts occurence.to_s }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
command :replace do |c|
|
18
|
+
c.syntax = 'i18n replace [key] [replacement] --index --verbose'
|
19
|
+
c.summary = 'Replace keys passed to I18n.t() by something else'
|
20
|
+
c.example 'i18n replace', 'i18n replace foo bar --index'
|
21
|
+
c.option '--index', 'Use an index'
|
22
|
+
c.option '--verbose', 'Output information about index build'
|
23
|
+
c.when_called do |args, options|
|
24
|
+
search, replacement = args.shift, args.shift
|
25
|
+
raise Commander::Runner::InvalidCommandError.new('Wrong number of arguments') unless search && replacement
|
26
|
+
I18n::Keys.verbose = options.verbose
|
27
|
+
index = I18n::Keys.index(:index => options.index)
|
28
|
+
index.each(search.dup, replacement.dup) do |occurence|
|
29
|
+
index.replace(occurence, replacement) if I18n::Commands.replace?(occurence, replacement)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module I18n
|
35
|
+
module Commands
|
36
|
+
class << self
|
37
|
+
def replace?(occurence, replacement)
|
38
|
+
return true if @all
|
39
|
+
answer = I18n::Commands.confirm_replacement(occurence, replacement)[0, 1]
|
40
|
+
answer == 'a' ? @all = true : answer == 'y'
|
41
|
+
end
|
42
|
+
|
43
|
+
def confirm_replacement(occurence, replacement)
|
44
|
+
puts occurence.to_s, occurence.context
|
45
|
+
msg = "Replace this occurence of the key \"#{occurence.key}\" with \"#{replacement}\"? [Y]es [N]o [A]ll"
|
46
|
+
answer = ask(msg, %w(y yes n no a all)) do |q|
|
47
|
+
q.case = :downcase
|
48
|
+
q.readline = true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module I18n
|
2
|
+
module Keys
|
3
|
+
class Index
|
4
|
+
module Formatter
|
5
|
+
module Setup
|
6
|
+
def setup(target)
|
7
|
+
(class << target; self; end).send(:include, self)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Stdin
|
12
|
+
extend Setup
|
13
|
+
|
14
|
+
def build(*args)
|
15
|
+
puts "building index \"#{name}\"" if I18n::Keys.verbose?
|
16
|
+
super
|
17
|
+
puts "\nfound #{occurences.size} occurences of #{keys.size} keys in #{files.size} files" if I18n::Keys.verbose?
|
18
|
+
end
|
19
|
+
|
20
|
+
def save
|
21
|
+
puts "saving index \"#{name}\"" if I18n::Keys.verbose?
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(file)
|
26
|
+
# puts " parsing #{file}" if I18n::Keys.verbose?
|
27
|
+
putc '.' if I18n::Keys.verbose?
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
# def each(*keys)
|
32
|
+
# puts "iterating occurences of: #{keys.map { |key| key.to_sym.inspect }.join(', ')}" if I18n::Keys.verbose?
|
33
|
+
# super
|
34
|
+
# end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require File.dirname(__FILE__) + '/formatter'
|
3
|
+
require File.dirname(__FILE__) + '/occurence'
|
4
|
+
|
5
|
+
# Keys.index(:default, :pattern => '/**/*.{rb,erb}').each(:foo, :bar) { |occurence| ... }
|
6
|
+
#
|
7
|
+
# when the :index name is not given it creates the index on the fly but won't save it
|
8
|
+
# when the :index name is true it uses :default as an index name
|
9
|
+
#
|
10
|
+
# when an index with the given name does not exist it builds and saves an index
|
11
|
+
# when an index with the given name exists and the given pattern does not match
|
12
|
+
# the index's pattern it issues a warning and rebuilds the index
|
13
|
+
#
|
14
|
+
# when no keys are given, occurences of all keys will be iterated
|
15
|
+
# when keys are given, only occurences of the given keys will be iterated
|
16
|
+
|
17
|
+
module I18n
|
18
|
+
module Keys
|
19
|
+
class Index
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
@@formatter = Formatter::Stdin
|
23
|
+
@@default_pattern = '/**/*.{rb,erb}'
|
24
|
+
|
25
|
+
class << self
|
26
|
+
def load_or_create_or_init(*args)
|
27
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
28
|
+
name = TrueClass === args.first ? :default : args.first
|
29
|
+
index = name ? load_or_create(name, options) : new(name, options)
|
30
|
+
index
|
31
|
+
end
|
32
|
+
|
33
|
+
def load_or_create(*args)
|
34
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
35
|
+
name = args.first || :default
|
36
|
+
exists?(name) ? load(name) : create(name, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
def create(*args)
|
40
|
+
index = new(*args)
|
41
|
+
index.update
|
42
|
+
index
|
43
|
+
end
|
44
|
+
|
45
|
+
def load(name)
|
46
|
+
File.open(filename(name), 'r') { |f| Marshal.load(f) } if exists?
|
47
|
+
end
|
48
|
+
|
49
|
+
def mk_dir
|
50
|
+
FileUtils.mkdir_p(store_dir) unless exists?
|
51
|
+
end
|
52
|
+
|
53
|
+
def exists?(name = nil)
|
54
|
+
name ? File.exists?(filename(name)) : File.exists?(store_dir)
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete(name)
|
58
|
+
new(name).delete
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete_all
|
62
|
+
FileUtils.rm_r(store_dir) if exists? rescue Errno::ENOENT
|
63
|
+
end
|
64
|
+
|
65
|
+
def filename(name)
|
66
|
+
store_dir + "/#{name.to_s}.marshal"
|
67
|
+
end
|
68
|
+
|
69
|
+
def store_dir
|
70
|
+
File.expand_path(Keys.meta_dir + '/indizes')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_reader :name
|
75
|
+
|
76
|
+
[:keys, :occurences, :by_key].each do |name|
|
77
|
+
class_eval <<-code
|
78
|
+
def #{name} # def key
|
79
|
+
build unless built? # build unless built?
|
80
|
+
@#{name} # @key
|
81
|
+
end # end
|
82
|
+
code
|
83
|
+
end
|
84
|
+
|
85
|
+
def initialize(*args)
|
86
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
87
|
+
@name = args.first || :default
|
88
|
+
@pattern = options[:pattern] || @@default_pattern
|
89
|
+
reset!
|
90
|
+
@@formatter.setup(self) if @@formatter
|
91
|
+
end
|
92
|
+
|
93
|
+
def reset!
|
94
|
+
@built = false
|
95
|
+
@keys = []
|
96
|
+
@occurences = []
|
97
|
+
@by_key = {}
|
98
|
+
end
|
99
|
+
|
100
|
+
def built?
|
101
|
+
@built
|
102
|
+
end
|
103
|
+
|
104
|
+
def build(options = {})
|
105
|
+
@occurences = find_occurences(options)
|
106
|
+
@occurences.each do |occurence|
|
107
|
+
@keys << occurence.key
|
108
|
+
(@by_key[occurence.key] ||= []) << occurence
|
109
|
+
end
|
110
|
+
@built = true
|
111
|
+
end
|
112
|
+
|
113
|
+
def exists?
|
114
|
+
File.exists?(filename)
|
115
|
+
end
|
116
|
+
|
117
|
+
def save
|
118
|
+
self.class.mk_dir
|
119
|
+
File.open(filename, 'w+') { |f| Marshal.dump(self, f) }
|
120
|
+
end
|
121
|
+
|
122
|
+
def update(options = {})
|
123
|
+
reset!
|
124
|
+
build(options)
|
125
|
+
save
|
126
|
+
end
|
127
|
+
|
128
|
+
def delete
|
129
|
+
FileUtils.rm(filename) if exists? rescue Errno::ENOENT
|
130
|
+
end
|
131
|
+
|
132
|
+
def filename
|
133
|
+
self.class.filename(name)
|
134
|
+
end
|
135
|
+
|
136
|
+
def files
|
137
|
+
Dir[Keys.root + pattern]
|
138
|
+
end
|
139
|
+
|
140
|
+
def pattern
|
141
|
+
@pattern ||= config['pattern'] || @@default_pattern
|
142
|
+
end
|
143
|
+
|
144
|
+
def config
|
145
|
+
@config ||= Keys.config['indices'][name] || { }
|
146
|
+
end
|
147
|
+
|
148
|
+
def each(*keys)
|
149
|
+
patterns = key_patterns(keys)
|
150
|
+
occurences.each { |occurence| yield(occurence) if key_matches?(occurence.key, patterns) }
|
151
|
+
end
|
152
|
+
|
153
|
+
def inject(memo, *keys)
|
154
|
+
each(*keys) { |occurence| yield(memo, occurence) }
|
155
|
+
memo
|
156
|
+
end
|
157
|
+
|
158
|
+
def replace!(occurence, replacement)
|
159
|
+
occurence.replace!(replacement)
|
160
|
+
save if exists?
|
161
|
+
end
|
162
|
+
|
163
|
+
def marshal_dump
|
164
|
+
keys = :name, :pattern, :keys, :occurences, :by_key
|
165
|
+
keys.inject({ :built => built? }) { |result, key| result[key] = send(key); result }
|
166
|
+
end
|
167
|
+
|
168
|
+
def marshal_load(data)
|
169
|
+
keys = :name, :pattern, :keys, :occurences, :by_key, :built
|
170
|
+
keys.each { |key| instance_variable_set(:"@#{key}", data[key]) }
|
171
|
+
end
|
172
|
+
|
173
|
+
protected
|
174
|
+
|
175
|
+
def find_occurences(options)
|
176
|
+
files.inject([]) do |result, file|
|
177
|
+
calls = parse(file).find_by_type(:call).select { |call| call[2] == :t }
|
178
|
+
calls.inject(result) do |result, node|
|
179
|
+
node.each_key_node { |key| result << Occurence.from_sexp(key, file) }
|
180
|
+
result
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def key_matches?(subject, key_patterns)
|
186
|
+
key_patterns.empty? || key_patterns.any? do |key, pattern|
|
187
|
+
subject.to_sym == key || subject.to_s =~ pattern
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def key_patterns(keys)
|
192
|
+
keys.inject({}) { |result, key| result[key] = key_pattern(key); result }
|
193
|
+
end
|
194
|
+
|
195
|
+
def key_pattern(key)
|
196
|
+
key = key.to_s.dup
|
197
|
+
match_end = key.gsub!(/\*$/, '') ? '' : '$'
|
198
|
+
pattern = Regexp.escape("#{key}")
|
199
|
+
/^#{pattern}#{match_end}/
|
200
|
+
end
|
201
|
+
|
202
|
+
def parse(file)
|
203
|
+
source = File.read(file)
|
204
|
+
source = ErbParser.new.to_ruby(source) if File.extname(file) == '.erb'
|
205
|
+
RubyParser.new.parse(source)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../ansi'
|
2
|
+
|
3
|
+
module I18n
|
4
|
+
module Keys
|
5
|
+
class Occurence
|
6
|
+
include Ansi
|
7
|
+
|
8
|
+
@@context_lines = 2
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def context_lines
|
12
|
+
@@context_lines
|
13
|
+
end
|
14
|
+
|
15
|
+
def context_lines=(num)
|
16
|
+
@@context_lines = num
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_sexp(sexp, file = nil)
|
20
|
+
new(sexp[1], file || sexp.file, sexp.source_start_pos, sexp.source_end_pos)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :key, :file, :start_pos, :end_pos
|
25
|
+
|
26
|
+
def initialize(key, file, start_pos, end_pos)
|
27
|
+
@key = key.to_sym
|
28
|
+
@file = file
|
29
|
+
@start_pos = start_pos
|
30
|
+
@end_pos = end_pos
|
31
|
+
end
|
32
|
+
|
33
|
+
def replace!(replacement)
|
34
|
+
replacement = replacement.to_sym.inspect
|
35
|
+
content = content_head + replacement + content_tail
|
36
|
+
@key = replacement.to_sym
|
37
|
+
self.length = replacement.to_s.length
|
38
|
+
File.open(file, 'w+') { |f| f.write(content) }
|
39
|
+
@position, @lines, @context = nil, nil, nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def content
|
43
|
+
lines.join
|
44
|
+
end
|
45
|
+
|
46
|
+
def lines
|
47
|
+
@lines ||= File.open(file, 'r') { |f| f.readlines }
|
48
|
+
end
|
49
|
+
|
50
|
+
def line(highlight = false)
|
51
|
+
line = lines[line_num - 1].dup
|
52
|
+
highlight ? line_head + ansi_format(original_key, [:red, :bold]) + line_tail : line
|
53
|
+
end
|
54
|
+
|
55
|
+
def line_num
|
56
|
+
position[0]
|
57
|
+
end
|
58
|
+
|
59
|
+
def column
|
60
|
+
position[1]
|
61
|
+
end
|
62
|
+
|
63
|
+
def length
|
64
|
+
end_pos - start_pos + 1
|
65
|
+
end
|
66
|
+
|
67
|
+
def length=(length)
|
68
|
+
@length = length
|
69
|
+
@end_pos = start_pos + length - 1
|
70
|
+
end
|
71
|
+
|
72
|
+
def original_key
|
73
|
+
line[column - 1, length]
|
74
|
+
end
|
75
|
+
|
76
|
+
def content_head
|
77
|
+
content[0..(start_pos - 1)].to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def content_tail
|
81
|
+
content[(end_pos + 1)..-1].to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def line_head
|
85
|
+
line[0..(column - 2)].to_s
|
86
|
+
end
|
87
|
+
|
88
|
+
def line_tail
|
89
|
+
line[(column + length - 1)..-1].to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
def context
|
93
|
+
@context ||=
|
94
|
+
lines[line_num - self.class.context_lines - 1, self.class.context_lines].join +
|
95
|
+
line(true) + lines[line_num, self.class.context_lines].join
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
"#{key}: #{file} [#{line_num}/#{column}]"
|
100
|
+
end
|
101
|
+
|
102
|
+
def ==(other)
|
103
|
+
key == other.key and start_pos == other.start_pos and end_pos == other.end_pos
|
104
|
+
end
|
105
|
+
|
106
|
+
def position
|
107
|
+
@position ||= begin
|
108
|
+
line_num, col = 1, 1
|
109
|
+
lines.inject(0) do |sum, line|
|
110
|
+
break if sum + line.length > start_pos
|
111
|
+
line_num += 1
|
112
|
+
col = start_pos - sum - line.length + 1
|
113
|
+
sum += line.length
|
114
|
+
end
|
115
|
+
[line_num, col]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# replaces html and erb tags with whitespace so that we can parse the result
|
2
|
+
# as pure ruby preserving the exact positions of tokens in the original erb
|
3
|
+
# source code
|
4
|
+
|
5
|
+
require 'erb'
|
6
|
+
$KCODE = 'u'
|
7
|
+
|
8
|
+
class String
|
9
|
+
def to_whitespace
|
10
|
+
gsub(/\S/, ' ')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module I18n
|
15
|
+
class ErbParser
|
16
|
+
class Scanner < ERB::Compiler::Scanner
|
17
|
+
def scan
|
18
|
+
stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m
|
19
|
+
etag_reg = /(.*?)(%%>|-%>|%>|\z)/m
|
20
|
+
scanner = StringScanner.new(@src)
|
21
|
+
while !scanner.eos?
|
22
|
+
scanner.scan(@stag ? etag_reg : stag_reg)
|
23
|
+
yield(scanner[1]) unless scanner[1].nil?
|
24
|
+
yield(scanner[2]) unless scanner[2].nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
ERB::Compiler::Scanner.regist_scanner(Scanner, nil, false)
|
29
|
+
|
30
|
+
def to_ruby(source)
|
31
|
+
result = ''
|
32
|
+
comment = false
|
33
|
+
scanner = ERB::Compiler.new(nil).make_scanner(source)
|
34
|
+
scanner.scan do |token|
|
35
|
+
comment = true if token == '<%#'
|
36
|
+
if scanner.stag.nil?
|
37
|
+
result << token.to_whitespace
|
38
|
+
scanner.stag = token if ['<%', '<%-', '<%=', '<%#', "\n"].include?(token)
|
39
|
+
elsif ['%>', '-%>'].include?(token)
|
40
|
+
result << token.to_whitespace
|
41
|
+
scanner.stag = nil
|
42
|
+
else
|
43
|
+
result << (comment ? token.to_whitespace : token) # so, this is the ruby code, then
|
44
|
+
comment = false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def content_dump(string)
|
51
|
+
string.split("\n").map { |s| s.to_whitespace }.join("\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'sexp_processor'
|
3
|
+
require 'ruby_parser'
|
4
|
+
|
5
|
+
# monkey patch galore, adds the ability to inspect the original source code
|
6
|
+
# that a :lit or :str sexp was parsed from
|
7
|
+
|
8
|
+
Sexp.class_eval do
|
9
|
+
attr_accessor :full_source, :source_start_pos, :source_end_pos
|
10
|
+
|
11
|
+
def value
|
12
|
+
self[1]
|
13
|
+
end
|
14
|
+
|
15
|
+
def source_range
|
16
|
+
source_start_pos..source_end_pos if source_start_pos && source_end_pos
|
17
|
+
end
|
18
|
+
|
19
|
+
def source
|
20
|
+
full_source[source_range] if full_source && source_range
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_by_type(type)
|
24
|
+
nodes = []
|
25
|
+
nodes << self if self.first == type
|
26
|
+
each_of_type(type) { |node| nodes << node }
|
27
|
+
nodes
|
28
|
+
end
|
29
|
+
|
30
|
+
def each_key_node(&block)
|
31
|
+
each do |element|
|
32
|
+
next unless Sexp === element
|
33
|
+
element.each_key_node(&block)
|
34
|
+
block.call(element) if element.is_key_node?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def is_key_node?
|
39
|
+
first == :str || first == :lit && self[1].is_a?(Symbol)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
RubyLexer.class_eval do
|
44
|
+
attr_accessor :source_start_pos, :source_end_pos
|
45
|
+
|
46
|
+
def reset_source_positions!
|
47
|
+
self.source_start_pos, self.source_end_pos = nil, nil, nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module I18n
|
52
|
+
class RubyParser < RubyParser
|
53
|
+
def s(*args)
|
54
|
+
result = super
|
55
|
+
|
56
|
+
result.full_source = lexer.src.string.dup
|
57
|
+
result.source_start_pos = lexer.source_start_pos
|
58
|
+
result.source_end_pos = lexer.source_end_pos || lexer.src.pos
|
59
|
+
lexer.reset_source_positions!
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
64
|
+
# :tSYMBOL
|
65
|
+
def _reduce_380(val, _values, result)
|
66
|
+
lexer.source_start_pos = lexer.src.pos - lexer.yacc_value.size - 1
|
67
|
+
lexer.source_end_pos = lexer.src.pos - 1
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
# :tSTRING
|
72
|
+
def _reduce_386(val, _values, result)
|
73
|
+
lexer.source_start_pos = lexer.src.pos - lexer.yacc_value.size - 2
|
74
|
+
lexer.source_end_pos = lexer.src.pos - 1
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
# :tSYMBEG
|
79
|
+
def _reduce_403(val, _values, result)
|
80
|
+
lexer.source_start_pos = lexer.src.pos - lexer.yacc_value.size - 2
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
def _reduce_418(val, _values, result)
|
85
|
+
result = super
|
86
|
+
result.source_start_pos = val[1].source_start_pos
|
87
|
+
result.source_end_pos = val[1].source_end_pos
|
88
|
+
result.full_source = val[1].full_source
|
89
|
+
result
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
data/test/all.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/*_test.rb'].each { |file| require file }
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
class ErbParserTest < Test::Unit::TestCase
|
5
|
+
def test_sexp_filename
|
6
|
+
erb = File.read("#{File.dirname(__FILE__)}/fixtures/template.html.erb")
|
7
|
+
ruby = I18n::ErbParser.new.to_ruby(erb)
|
8
|
+
assert_equal erb.length, ruby.length
|
9
|
+
%w(erb_1 erb_2 foo.erb_3).each do |key|
|
10
|
+
assert ruby.index(key)
|
11
|
+
assert_equal erb.index(key), ruby.index(key)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
t(:bar_2)
|
data/test/index_test.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class IndexTest < Test::Unit::TestCase
|
4
|
+
include I18n
|
5
|
+
|
6
|
+
def setup
|
7
|
+
I18n::Keys.verbose = false
|
8
|
+
@filenames = %W( #{File.dirname(__FILE__)}/fixtures/source_1.rb
|
9
|
+
#{File.dirname(__FILE__)}/fixtures/source_2.rb )
|
10
|
+
@default_keys_root, Keys.root = Keys.root, File.expand_path(File.dirname(__FILE__)) + '/fixtures'
|
11
|
+
FileUtils.cp(@filenames[0], "#{@filenames[0]}.backup")
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
Keys::Index.delete_all
|
16
|
+
Keys.root = @default_keys_root
|
17
|
+
FileUtils.mv("#{@filenames[0]}.backup", @filenames[0])
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_index_default_pattern
|
21
|
+
Keys.config = nil
|
22
|
+
assert_equal '/**/*.{rb,erb}', Keys::Index.new.pattern
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_index_finds_files
|
26
|
+
expected = %w(test/fixtures/source_1.rb test/fixtures/source_2.rb).map { |p| File.expand_path(p) }
|
27
|
+
assert_equal expected, Keys::Index.new.files & expected
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_occurences_built_lazily_on_a_fresh_index
|
31
|
+
index = Keys::Index.new(:default)
|
32
|
+
expected = [:bar, :baaar, :baar, :bar, :bar_1, :bar_2]
|
33
|
+
assert_equal expected, index.occurences.select { |key| expected.include?(key.key) }.map(&:key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_occurences_from_marshalled_index
|
37
|
+
index = Keys::Index.new(:marshalled)
|
38
|
+
index.update
|
39
|
+
index = Keys::Index.load(:marshalled)
|
40
|
+
expected = [:bar, :baaar, :baar, :bar, :bar_1, :bar_2]
|
41
|
+
assert_equal expected, index.occurences.select { |key| expected.include?(key.key) }.map(&:key)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_index_create_builds_and_saves_the_index
|
45
|
+
index = Keys::Index.new(:create)
|
46
|
+
assert !index.exists?
|
47
|
+
index = Keys::Index.create(:create, :pattern => '/**/*.{rb}')
|
48
|
+
assert index.built?
|
49
|
+
assert index.exists?
|
50
|
+
assert_equal '/**/*.{rb}', index.pattern
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_index_load_or_create_creates_an_index
|
54
|
+
index = Keys::Index.load_or_create(:load_or_create, :pattern => '/**/*.{rb}')
|
55
|
+
assert index.exists?
|
56
|
+
assert_equal '/**/*.{rb}', index.pattern
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_index_load_or_create_or_init_with_no_name_given_does_not_save_the_index
|
60
|
+
index = Keys::Index.load_or_create_or_init(:pattern => '/**/*.{rb}')
|
61
|
+
assert !index.exists?
|
62
|
+
assert_equal '/**/*.{rb}', index.pattern
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_index_load_or_create_or_init_with_true_given_saves_the_default_index
|
66
|
+
index = Keys::Index.load_or_create_or_init(true, :pattern => '/**/*.{rb}')
|
67
|
+
assert index.exists?
|
68
|
+
assert_equal :default, index.name
|
69
|
+
assert_equal '/**/*.{rb}', index.pattern
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_index_load_or_create_or_init_with_name_given_saves_the_named_index
|
73
|
+
index = Keys::Index.load_or_create_or_init(:foo, :pattern => '/**/*.{rb}')
|
74
|
+
assert index.exists?
|
75
|
+
assert_equal :foo, index.name
|
76
|
+
assert_equal '/**/*.{rb}', index.pattern
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_index_exists_is_true_when_index_directory_exists
|
80
|
+
index = Keys::Index.create('foo')
|
81
|
+
assert index.exists?
|
82
|
+
FileUtils.rm_r(index.filename)
|
83
|
+
assert !index.exists?
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_index_inject_with_no_keys_given_iterates_over_all_occurences
|
87
|
+
index = Keys::Index.new(:pattern => '/**/*.{rb}')
|
88
|
+
expected = [:bar, :baaar, :baar, :'foo.bar', :bar, :bar_1, :bar_2]
|
89
|
+
result = index.inject([]) { |result, occurence| result << occurence.key }
|
90
|
+
assert_equal expected, result
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_index_inject_with_keys_given_iterates_over_occurences_of_given_keys
|
94
|
+
index = Keys::Index.new(:pattern => '/**/*.{rb}')
|
95
|
+
expected = [:bar, :baaar, :baar, :bar]
|
96
|
+
result = index.inject([], :bar, :baaar, :baar) { |result, occurence| result << occurence.key }
|
97
|
+
assert_equal expected, result
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_key_included_with_no_keys_given_returns_true
|
101
|
+
index = Keys::Index.new
|
102
|
+
assert index.send(:key_matches?, :foo, [])
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_key_included_with_the_key_included_returns_true
|
106
|
+
index = Keys::Index.new
|
107
|
+
assert index.send(:key_matches?, :foo, { :foo => /^foo$/, :bar => /^bar$/ })
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_key_pattern_with_no_wild_cards_returns_a_pattern_matching_only_the_key
|
111
|
+
index = Keys::Index.new
|
112
|
+
assert_equal /^foo\.bar$/, index.send(:key_pattern, :'foo.bar')
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_key_pattern_with_a_dot_separated_wild_card_at_the_end_returns_a_pattern_matching_all_keys_starting_with_key
|
116
|
+
index = Keys::Index.new
|
117
|
+
assert_equal /^foo\.bar\./, index.send(:key_pattern, :'foo.bar.*')
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_replace_replaces_key_without_wildcard_in_source_file
|
121
|
+
index = Keys::Index.create(:replace)
|
122
|
+
bar = index.by_key[:bar].first
|
123
|
+
index.replace!(bar, 'foo')
|
124
|
+
assert_equal " t(:foo)\n", bar.line
|
125
|
+
|
126
|
+
index = Keys::Index.load(:replace)
|
127
|
+
foo = index.by_key[:bar].first
|
128
|
+
assert foo == bar
|
129
|
+
end
|
130
|
+
|
131
|
+
# TODO
|
132
|
+
# - after replace make sure the index is updated
|
133
|
+
# - somehow also update the yaml/rb files
|
134
|
+
|
135
|
+
end
|
data/test/keys_test.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class KeysTest < Test::Unit::TestCase
|
4
|
+
include I18n
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@filenames = %W( #{File.dirname(__FILE__)}/fixtures/source_1.rb
|
8
|
+
#{File.dirname(__FILE__)}/fixtures/source_2.rb )
|
9
|
+
@default_keys_root, Keys.root = Keys.root, File.expand_path(File.dirname(__FILE__))
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
Keys::Index.delete_all
|
14
|
+
Keys.root = @default_keys_root
|
15
|
+
Keys.config = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_root_defaults_to_dot
|
19
|
+
assert_equal '.', @default_keys_root
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_can_set_root
|
23
|
+
old_root = I18n::Keys.root
|
24
|
+
assert_nothing_raised { I18n::Keys.root = 'path/to/root' }
|
25
|
+
assert_equal 'path/to/root', I18n::Keys.root
|
26
|
+
I18n::Keys.root = old_root
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_index_given_no_name_returns_an_unbuilt_and_unsaved_index
|
30
|
+
index = Keys.index
|
31
|
+
assert_equal Keys::Index, index.class
|
32
|
+
assert !index.built?
|
33
|
+
assert !index.exists?
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_index_given_true_as_a_name_returns_the_built_default_index
|
37
|
+
index = Keys.index(true)
|
38
|
+
assert_equal Keys::Index, index.class
|
39
|
+
assert_equal :default, index.name
|
40
|
+
assert index.built?
|
41
|
+
assert index.exists?
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_index_given_a_regular_name_returns_the_built_named_index
|
45
|
+
index = Keys.index(:foo)
|
46
|
+
assert_equal Keys::Index, index.class
|
47
|
+
assert_equal :foo, index.name
|
48
|
+
assert index.built?
|
49
|
+
assert index.exists?
|
50
|
+
end
|
51
|
+
|
52
|
+
# def test_find_all_in_file
|
53
|
+
# expected = [:bar, :baaar, :baar, "bar", "bar_1"]
|
54
|
+
# keys = I18n::Keys.find(:files => @filenames[0])
|
55
|
+
# assert_equal expected, keys.select { |key| expected.include?(key.key) }.map(&:key)
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def test_find_all_in_files
|
59
|
+
# expected = ['bar_1', :bar_2]
|
60
|
+
# keys = I18n::Keys.find(:files => @filenames)
|
61
|
+
# assert_equal expected, keys.select { |key| expected.include?(key.key) }.map(&:key)
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# def test_find_key_in_file
|
65
|
+
# expected = [:bar]
|
66
|
+
# keys = I18n::Keys.find(:keys => :bar, :files => @filenames[0])
|
67
|
+
# assert_equal expected, keys.select { |key| expected.include?(key.key) }.map(&:key)
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# def test_find_key_in_files
|
71
|
+
# expected = [:bar]
|
72
|
+
# keys = I18n::Keys.find(:keys => :bar, :files => @filenames)
|
73
|
+
# assert_equal expected, keys.select { |key| expected.include?(key.key) }.map(&:key)
|
74
|
+
# end
|
75
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class OccurenceTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
I18n::Keys.verbose = false
|
6
|
+
end
|
7
|
+
|
8
|
+
def setup
|
9
|
+
I18n::Keys.verbose = false
|
10
|
+
@filename = "#{File.dirname(__FILE__)}/fixtures/source_1.rb"
|
11
|
+
FileUtils.cp(@filename, "#{@filename}.backup")
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
FileUtils.mv("#{@filename}.backup", @filename)
|
16
|
+
end
|
17
|
+
|
18
|
+
def occurence(key = :bar)
|
19
|
+
index = I18n::Keys::Index.new
|
20
|
+
index.update
|
21
|
+
index.by_key[key.to_sym].first
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_line_without_highlighting
|
25
|
+
assert_equal " t(:'baar')\n", occurence(:baar).line
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_line_with_highlighting
|
29
|
+
assert_equal " t(\e[0;31;1m:'baar'\e[0m)\n", occurence(:baar).line(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_occurence_line_num
|
33
|
+
assert_equal 3, occurence.line_num
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_occurence_column
|
37
|
+
assert_equal 7, occurence.column
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_occurence_length
|
41
|
+
assert_equal 4, occurence.length
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_original_key
|
45
|
+
assert ":bar", occurence.original_key
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_content_head
|
49
|
+
assert_equal "def foo\n t(", occurence.content_head[-14, 999]
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_content_tail
|
53
|
+
assert_equal ")\n t(:\"baaar\")\n", occurence.content_tail[0, 18]
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_line_head
|
57
|
+
assert_equal " t(", occurence.line_head
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_line_tail
|
61
|
+
assert_equal ")\n", occurence.line_tail
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_context_returns_the_context_of_the_occurence_with_2_lines_setting
|
65
|
+
context = occurence(:baar).context.split("\n")
|
66
|
+
assert_equal 5, context.length
|
67
|
+
assert_equal " t(\e[0;31;1m:'baar'\e[0m)", context[2]
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_context_returns_the_context_of_the_occurence_with_3_lines_setting
|
71
|
+
I18n::Keys::Occurence.context_lines = 3
|
72
|
+
context = occurence(:baar).context.split("\n")
|
73
|
+
assert_equal 7, context.length
|
74
|
+
assert_equal " t(\e[0;31;1m:'baar'\e[0m)", context[3]
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_replace_simple_symbol_with_simple_symbol
|
78
|
+
bar = occurence(:bar)
|
79
|
+
bar.replace!(:oooooooo)
|
80
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_replace_simple_symbol_with_quoted_symbol
|
84
|
+
bar = occurence(:bar)
|
85
|
+
bar.replace!(:'oooo.oooo')
|
86
|
+
assert_equal " t(\e[0;31;1m:\"oooo.oooo\"\e[0m)\n", bar.line(true)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_replace_simple_symbol_with_string
|
90
|
+
bar = occurence(:bar)
|
91
|
+
bar.replace!('oooooooo')
|
92
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_replace_quoted_symbol_with_simple_symbol
|
96
|
+
bar = occurence(:'foo.bar')
|
97
|
+
bar.replace!(:oooooooo)
|
98
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_replace_quoted_symbol_with_quoted_symbol
|
102
|
+
bar = occurence(:'foo.bar')
|
103
|
+
bar.replace!(:'oooo.oooo')
|
104
|
+
assert_equal " t(\e[0;31;1m:\"oooo.oooo\"\e[0m)\n", bar.line(true)
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_replace_quoted_symbol_with_string
|
108
|
+
bar = occurence(:'foo.bar')
|
109
|
+
bar.replace!('oooooooo')
|
110
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_replace_string_with_simple_symbol
|
114
|
+
bar = occurence('bar_1')
|
115
|
+
bar.replace!(:oooooooo)
|
116
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_replace_string_with_quoted_symbol
|
120
|
+
bar = occurence('bar_1')
|
121
|
+
bar.replace!(:'oooo.oooo')
|
122
|
+
assert_equal " t(\e[0;31;1m:\"oooo.oooo\"\e[0m)\n", bar.line(true)
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_replace_string_with_string
|
126
|
+
bar = occurence('bar_1')
|
127
|
+
bar.replace!('oooooooo')
|
128
|
+
assert_equal " t(\e[0;31;1m:oooooooo\e[0m)\n", bar.line(true)
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class RubyParserTest < Test::Unit::TestCase
|
4
|
+
def test_sexp_filename
|
5
|
+
filename = File.dirname(__FILE__) + '/fixtures/source_1.rb'
|
6
|
+
sexp = I18n::RubyParser.new.parse(File.read(filename), filename)
|
7
|
+
assert_equal filename, sexp.file
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_sexp_source_positions
|
11
|
+
assert_equal 2, 't(:foo)'.to_sexp.find_node(:lit).source_start_pos
|
12
|
+
assert_equal 5, 't(:foo)'.to_sexp.find_node(:lit).source_end_pos
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_sexp_source_symbol
|
16
|
+
assert_equal ':foo', 't(:foo)'.to_sexp.find_node(:lit).source
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_sexp_source_symbol_and_options
|
20
|
+
assert_equal ':foo', 't(:foo, :bar => :baz)'.to_sexp.find_node(:lit).source
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_sexp_source_symbol_with_double_quotes
|
24
|
+
assert_equal ':"foo"', 't(:"foo")'.to_sexp.find_node(:lit).source
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_sexp_source_symbol_with_double_quotes_and_options
|
28
|
+
assert_equal ':"foo"', 't(:"foo", :bar => :baz)'.to_sexp.find_node(:lit).source
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_sexp_source_symbol_with_single_quotes
|
32
|
+
assert_equal ":'foo'", "t(:'foo')".to_sexp.find_node(:lit).source
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_sexp_source_symbol_with_single_quotes_and_options
|
36
|
+
assert_equal ":'foo'", "t(:'foo', :bar => :baz)".to_sexp.find_node(:lit).source
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_sexp_source_string_with_double_quotes
|
40
|
+
assert_equal '"foo"', 't("foo")'.to_sexp.find_node(:str).source
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_sexp_source_string_with_double_quotes_and_options
|
44
|
+
assert_equal '"foo"', 't("foo", :bar => :baz)'.to_sexp.find_node(:str).source
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_sexp_source_string_with_single_quotes
|
48
|
+
assert_equal "'foo'", "t('foo')".to_sexp.find_node(:str).source
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_sexp_source_string_with_single_quotes_and_options
|
52
|
+
assert_equal "'foo'", "t('foo', :bar => :baz)".to_sexp.find_node(:str).source
|
53
|
+
end
|
54
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: svenfuchs-i18n-tools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sven Fuchs
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-26 00:00:00 -07:00
|
13
|
+
default_executable: i18n-keys
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: visionmedia-commander
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - "="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.5.7
|
24
|
+
version:
|
25
|
+
description: Tools for Ruby/Rails I18n
|
26
|
+
email: rails-i18n@googlegroups.com
|
27
|
+
executables:
|
28
|
+
- i18n-keys
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- bin/i18n-keys
|
35
|
+
- lib/ansi.rb
|
36
|
+
- lib/i18n/keys/commands.rb
|
37
|
+
- lib/i18n/keys/formatter.rb
|
38
|
+
- lib/i18n/keys/index.rb
|
39
|
+
- lib/i18n/keys/occurence.rb
|
40
|
+
- lib/i18n/keys.rb
|
41
|
+
- lib/i18n/parser/erb_parser.rb
|
42
|
+
- lib/i18n/parser/ruby_parser.rb
|
43
|
+
- MIT-LICENSE
|
44
|
+
- README.textile
|
45
|
+
has_rdoc: false
|
46
|
+
homepage: http://rails-i18n.org
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.2.0
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: Tools for Ruby/Rails I18n
|
71
|
+
test_files:
|
72
|
+
- test/all.rb
|
73
|
+
- test/commands_test.rb
|
74
|
+
- test/erb_parser_test.rb
|
75
|
+
- test/fixtures/source_1.rb
|
76
|
+
- test/fixtures/source_2.rb
|
77
|
+
- test/fixtures/template.html.erb
|
78
|
+
- test/index_test.rb
|
79
|
+
- test/keys_test.rb
|
80
|
+
- test/occurence_test.rb
|
81
|
+
- test/ruby_parser_test.rb
|
82
|
+
- test/test_helper.rb
|