i18n-tools 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/lib/core_ext/hash/iterate_nested.rb +35 -0
  2. data/lib/core_ext/hash/slice.rb +20 -0
  3. data/lib/core_ext/hash/sorted_yaml_style.rb +17 -0
  4. data/lib/core_ext/hash/symbolize_keys.rb +14 -0
  5. data/lib/core_ext/module/attribute_accessors.rb +48 -0
  6. data/lib/core_ext/object/deep_clone.rb +5 -0
  7. data/lib/core_ext/object/instance_variables.rb +9 -0
  8. data/lib/core_ext/object/meta_class.rb +5 -0
  9. data/lib/core_ext/object/tap.rb +6 -0
  10. data/lib/i18n/backend/simple_storage.rb +119 -0
  11. data/lib/i18n/commands/keys.rb +84 -0
  12. data/lib/i18n/exceptions/key_exists.rb +9 -0
  13. data/lib/i18n/index.rb +33 -0
  14. data/lib/i18n/index/base.rb +38 -0
  15. data/lib/i18n/index/file.rb +55 -0
  16. data/lib/i18n/index/format.rb +49 -0
  17. data/lib/i18n/index/key.rb +45 -0
  18. data/lib/i18n/index/occurence.rb +18 -0
  19. data/lib/i18n/index/simple.rb +69 -0
  20. data/lib/i18n/index/simple/data.rb +34 -0
  21. data/lib/i18n/index/simple/storage.rb +79 -0
  22. data/lib/i18n/ripper2ruby.rb +7 -0
  23. data/lib/i18n/ripper2ruby/translate_args_list.rb +89 -0
  24. data/lib/i18n/ripper2ruby/translate_call.rb +66 -0
  25. data/lib/i18n/translation_properties.rb +38 -0
  26. data/test/all.rb +1 -1
  27. data/test/core_ext/hash_iterate_nested.rb +31 -0
  28. data/test/fixtures/all.rb.src +106 -0
  29. data/test/fixtures/config.yml +3 -0
  30. data/test/fixtures/locale/de.yml +4 -0
  31. data/test/fixtures/locale/en.yml +4 -0
  32. data/test/fixtures/source_1.rb +2 -1
  33. data/test/fixtures/translate/double_key.rb +32 -0
  34. data/test/fixtures/translate/double_scope.rb +32 -0
  35. data/test/fixtures/translate/single_key.rb +10 -0
  36. data/test/fixtures/translate/single_scope.rb +32 -0
  37. data/test/i18n/backend/simple_storage_test.rb +81 -0
  38. data/test/i18n/backend/translation_properties_test.rb +33 -0
  39. data/test/i18n/index/all.rb +1 -0
  40. data/test/i18n/index/args_replace_test.rb +218 -0
  41. data/test/i18n/index/calls_replace_test.rb +67 -0
  42. data/test/i18n/index/commands_test.rb +75 -0
  43. data/test/i18n/index/key_test.rb +32 -0
  44. data/test/i18n/index/simple_test.rb +67 -0
  45. data/test/i18n/ripper2ruby/translate_call_test.rb +98 -0
  46. data/test/test_helper.rb +66 -9
  47. metadata +49 -32
  48. data/MIT-LICENSE +0 -20
  49. data/README.textile +0 -1
  50. data/bin/i18n-keys +0 -6
  51. data/lib/ansi.rb +0 -19
  52. data/lib/i18n/keys.rb +0 -51
  53. data/lib/i18n/keys/commands.rb +0 -53
  54. data/lib/i18n/keys/formatter.rb +0 -39
  55. data/lib/i18n/keys/index.rb +0 -209
  56. data/lib/i18n/keys/occurence.rb +0 -120
  57. data/lib/i18n/parser/erb_parser.rb +0 -54
  58. data/lib/i18n/parser/ruby_parser.rb +0 -93
  59. data/test/commands_test.rb +0 -1
  60. data/test/erb_parser_test.rb +0 -31
  61. data/test/index_test.rb +0 -135
  62. data/test/keys_test.rb +0 -75
  63. data/test/occurence_test.rb +0 -130
  64. data/test/ruby_parser_test.rb +0 -54
@@ -1,39 +0,0 @@
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
@@ -1,209 +0,0 @@
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
- code = parse(file) || Sexp.new
178
- code.find_by_type(:call).select { |call| call[2] == :t }.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
@@ -1,120 +0,0 @@
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
@@ -1,54 +0,0 @@
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 ['<%', '<%-', '<%=', '<%#'].include?(token)
39
- elsif ['%>', '-%>'].include?(token)
40
- result << token.gsub(/>/, ';').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
@@ -1,93 +0,0 @@
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
-