i18n-tools 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-