vocab 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/.gitignore +9 -0
  2. data/Gemfile +4 -0
  3. data/README.md +58 -0
  4. data/Rakefile +13 -0
  5. data/bin/vocab +11 -0
  6. data/lib/vocab/application.rb +58 -0
  7. data/lib/vocab/extractor/android.rb +56 -0
  8. data/lib/vocab/extractor/base.rb +92 -0
  9. data/lib/vocab/extractor/rails.rb +81 -0
  10. data/lib/vocab/extractor.rb +7 -0
  11. data/lib/vocab/merger/android.rb +59 -0
  12. data/lib/vocab/merger/base.rb +21 -0
  13. data/lib/vocab/merger/rails.rb +105 -0
  14. data/lib/vocab/merger.rb +7 -0
  15. data/lib/vocab/settings.rb +35 -0
  16. data/lib/vocab/translator/android.rb +43 -0
  17. data/lib/vocab/translator/base.rb +12 -0
  18. data/lib/vocab/translator/rails.rb +67 -0
  19. data/lib/vocab/translator.rb +7 -0
  20. data/lib/vocab/ui.rb +19 -0
  21. data/lib/vocab/validator/android.rb +23 -0
  22. data/lib/vocab/validator/base.rb +40 -0
  23. data/lib/vocab/validator/rails.rb +31 -0
  24. data/lib/vocab/validator.rb +7 -0
  25. data/lib/vocab/version.rb +3 -0
  26. data/lib/vocab.rb +36 -0
  27. data/spec/application_spec.rb +20 -0
  28. data/spec/data/android/current.xml +8 -0
  29. data/spec/data/android/locales/values/strings.xml +10 -0
  30. data/spec/data/android/locales/values-es/strings.xml +6 -0
  31. data/spec/data/android/locales/values-large-hdpi/dimens.xml +1 -0
  32. data/spec/data/android/locales/values-large-hdpi/styles.xml +1 -0
  33. data/spec/data/android/previous.xml +6 -0
  34. data/spec/data/android/translations/values-es/es-string-file.xml +7 -0
  35. data/spec/data/android/write.xml +6 -0
  36. data/spec/data/rails/locales/en.yml +10 -0
  37. data/spec/data/rails/locales/es.yml +6 -0
  38. data/spec/data/rails/locales/models/product/en.yml +13 -0
  39. data/spec/data/rails/locales/models/product/es.yml +13 -0
  40. data/spec/data/rails/translations/cn.yml +21 -0
  41. data/spec/data/rails/translations/es.yml +12 -0
  42. data/spec/extractor/android_spec.rb +95 -0
  43. data/spec/extractor/base_spec.rb +134 -0
  44. data/spec/extractor/rails_spec.rb +116 -0
  45. data/spec/merger/android_spec.rb +138 -0
  46. data/spec/merger/base_spec.rb +24 -0
  47. data/spec/merger/rails_spec.rb +183 -0
  48. data/spec/spec_helper.rb +23 -0
  49. data/spec/support/matchers.rb +13 -0
  50. data/spec/support/shared.rb +6 -0
  51. data/spec/tmp/.gitkeep +0 -0
  52. data/spec/translator/android_spec.rb +24 -0
  53. data/spec/translator/rails_spec.rb +155 -0
  54. data/spec/ui_spec.rb +15 -0
  55. data/spec/validator/android_spec.rb +54 -0
  56. data/spec/validator/base_spec.rb +5 -0
  57. data/spec/validator/rails_spec.rb +83 -0
  58. data/spec/vocab_spec.rb +13 -0
  59. data/vocab.gemspec +25 -0
  60. metadata +171 -0
@@ -0,0 +1,67 @@
1
+ # Warning: this object has some sharp edges because it is an unusual use of the i18n gem
2
+ # For example:
3
+ # - we are calling private methods
4
+ # - instances share the I18n.load_path, which could lead to unexpected behavior
5
+
6
+ module Vocab
7
+ module Translator
8
+ class Rails < Base
9
+
10
+ attr_accessor :locale
11
+
12
+ def initialize( locale = :en )
13
+ @backend = I18n::Backend::Simple.new
14
+ @locale = locale
15
+ end
16
+
17
+ def load_dir( dir )
18
+ I18n.load_path = Dir.glob( "#{dir}/**/*.{yml,rb}" )
19
+ load_translations
20
+ end
21
+
22
+ def load_file( file )
23
+ @locale = File.basename( file ).gsub( /\..*$/, '' ).to_sym
24
+ I18n.load_path = [ file ]
25
+ load_translations
26
+ end
27
+
28
+ def write_file( file )
29
+ yaml = { @locale => self.translations }.to_yaml
30
+ File.open( file, 'w+' ) { |f| f.write( yaml ) }
31
+ end
32
+
33
+ def translations( options = {} )
34
+ t = @backend.send(:translations)
35
+ trans = t[ @locale ]
36
+ return options[ :prefix ] == true ? { @locale => trans } : trans
37
+ end
38
+
39
+ def flattened_translations( options = {} )
40
+ return @backend.flatten_translations( @locale, translations( options ), true, false )
41
+ end
42
+
43
+ def store( key, value )
44
+ keys = I18n.normalize_keys( @locale, key, [], nil)
45
+ keys.shift # remove locale
46
+ data = keys.reverse.inject( value ) { |result, _key| { _key => result } }
47
+ @backend.store_translations( @locale, data )
48
+ end
49
+
50
+ def fetch( key )
51
+ return flattened_translations[ key.to_sym ]
52
+ end
53
+
54
+ def self.en_equivalent_path( path )
55
+ return "#{File.dirname( path )}/en.yml"
56
+ end
57
+
58
+ private
59
+
60
+ def load_translations
61
+ @backend.reload!
62
+ @backend.send( :init_translations )
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,7 @@
1
+ module Vocab
2
+ module Translator
3
+ autoload :Android, 'vocab/translator/android'
4
+ autoload :Base, 'vocab/translator/base'
5
+ autoload :Rails, 'vocab/translator/rails'
6
+ end
7
+ end
data/lib/vocab/ui.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Vocab
2
+ class UI
3
+
4
+ attr_accessor :silent
5
+
6
+ def initialize()
7
+ @silent = false
8
+ end
9
+
10
+ def say( message )
11
+ puts message unless silent
12
+ end
13
+
14
+ def warn( message )
15
+ say( "Warning: #{message}" )
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Vocab
2
+ module Validator
3
+ class Android < Base
4
+
5
+ def initialize( locales_dir = nil )
6
+ @locales_dir = locales_dir || 'res'
7
+ end
8
+
9
+ def other_keys( path )
10
+ return Vocab::Translator::Android.hash_from_xml( path ).keys
11
+ end
12
+
13
+ def english_keys( path = nil )
14
+ return Vocab::Translator::Android.english_keys( @locales_dir )
15
+ end
16
+
17
+ def files_to_validate
18
+ return Dir.glob( "#{@locales_dir}/values-*/strings.xml" )
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ module Vocab
2
+ module Validator
3
+ class Base
4
+
5
+ def validate
6
+ files = files_to_validate
7
+ Vocab.ui.say( "#{files.size} file(s) to validate in #{@locales_dir}" )
8
+ files.each do |path|
9
+ validation = validate_file( path )
10
+ print( path, validation )
11
+ end
12
+ end
13
+
14
+ def print( path, validation )
15
+ Vocab.ui.say( "Validating: #{path}" )
16
+
17
+ unless validation[ :missing ].empty?
18
+ Vocab.ui.say( " Missing keys:" )
19
+ validation[ :missing ].each { |missing| Vocab.ui.say( " - #{missing}" ) }
20
+ end
21
+
22
+ unless validation[ :extra ].empty?
23
+ Vocab.ui.say( " Extra keys:" )
24
+ validation[ :extra ].each { |extra| Vocab.ui.say( " + #{extra}" ) }
25
+ end
26
+ end
27
+
28
+ def validate_file( path )
29
+ other = other_keys( path )
30
+ english = english_keys( path )
31
+
32
+ result = {}
33
+ result[ :missing ] = ( english - other ).sort
34
+ result[ :extra ] = ( other - english ).sort
35
+ return result
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ module Vocab
2
+ module Validator
3
+ class Rails < Base
4
+
5
+ def initialize( locales_dir = nil )
6
+ @locales_dir = locales_dir || 'config/locales'
7
+ end
8
+
9
+ def other_keys( path )
10
+ return flattened_keys( path )
11
+ end
12
+
13
+ def english_keys( path )
14
+ return flattened_keys( Vocab::Translator::Rails.en_equivalent_path( path ) )
15
+ end
16
+
17
+ def files_to_validate
18
+ return Dir.glob( "#{@locales_dir}/**/*.yml" ).reject { |path| File.basename( path ) == 'en.yml' }
19
+ end
20
+
21
+ protected
22
+
23
+ def flattened_keys( path )
24
+ translator = Vocab::Translator::Rails.new
25
+ translator.load_file( path )
26
+ return translator.flattened_translations.keys.collect { |key| key.to_s }
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ module Vocab
2
+ module Validator
3
+ autoload :Android, 'vocab/validator/android'
4
+ autoload :Base, 'vocab/validator/base'
5
+ autoload :Rails, 'vocab/validator/rails'
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Vocab
2
+ VERSION = "0.0.1"
3
+ end
data/lib/vocab.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'fileutils'
2
+ require 'i18n'
3
+ require 'pathname'
4
+ require 'nokogiri'
5
+
6
+ require 'vocab/application'
7
+ require 'vocab/extractor'
8
+ require 'vocab/translator'
9
+ require 'vocab/merger'
10
+ require 'vocab/settings'
11
+ require 'vocab/ui'
12
+ require 'vocab/validator'
13
+ require 'vocab/version'
14
+
15
+ I18n::Backend::Simple.send( :include, I18n::Backend::Flatten )
16
+
17
+ module Vocab
18
+
19
+ class << self
20
+ def settings
21
+ @settings ||= Settings.new( root )
22
+ end
23
+
24
+ def ui
25
+ @ui ||= UI.new
26
+ end
27
+
28
+ # Returns root directory where .vocab is located
29
+ # TODO: make this work in subdirectory under .vocab
30
+ def root
31
+ return File.expand_path( Dir.pwd )
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,20 @@
1
+ require "spec_helper"
2
+
3
+ describe "init" do
4
+
5
+ before( :each ) do
6
+ @tmpdir = File.join( vocab_root, "spec", "tmp" )
7
+ FileUtils.rm_rf( Dir.glob( "#{@tmpdir}/*" ) )
8
+ Dir.chdir( @tmpdir )
9
+ end
10
+
11
+ it "creates a .vocab file if one doesn't exist" do
12
+ Vocab.ui.should_receive( :say ).with( "Writing new .vocab file. Check this file into your project repo" )
13
+ Vocab::Settings.create
14
+ path = "#{@tmpdir}/.vocab"
15
+ File.exists?( path ).should be_true
16
+ YAML.load_file( path ).should == { "last_translation" => `git rev-parse HEAD`.strip }
17
+ `git rm -f #{path}`
18
+ end
19
+
20
+ end
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string name="app_name">Kid Mode</string>
4
+ <string name="pd_app_name">Parent Dashboard</string>
5
+ <string name="app_current">current</string>
6
+ <string name="cancel">Cancel</string>
7
+ <string name="delete">Delete</string>
8
+ </resources>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string name="app_name">Kid Mode</string>
4
+ <string name="pd_app_name">Parent Dashboard</string>
5
+ <!-- This is a comment -->
6
+ <string name="app_current">current</string>
7
+ <string name="cancel">Cancel</string>
8
+ <string name="delete">Delete</string>
9
+ <string name="not_in_es">This key not in spanish</string>
10
+ </resources>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string name="app_name">Modo Niños</string>
4
+ <string name="pd_app_name">el Panel para padres</string>
5
+ <string name="app_current">actual</string>
6
+ </resources>
@@ -0,0 +1 @@
1
+ This is just a decoy
@@ -0,0 +1 @@
1
+ This is just a decoy
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string name="app_name">Kid Mode</string>
4
+ <string name="pd_app_name">Parent Dashboard</string>
5
+ <string name="app_current">current</string>
6
+ </resources>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <resources>
3
+ <string name="cancel">Cancelar</string>
4
+ <string name="delete">La funci&oacute;n Child Lock</string>
5
+ <string name="pd_app_name">el Panel para padres bien</string>
6
+ <string name="translator_cruft">Malo</string>
7
+ </resources>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <resources>
3
+ <string name="app_name">Kid Mode</string>
4
+ <string name="delete">La funci&amp;#xF3;n Child Lock</string>
5
+ <string name="pd_app_name">Parent Dashboard</string>
6
+ </resources>
@@ -0,0 +1,10 @@
1
+ en:
2
+ marketing:
3
+ banner: This product is so good
4
+ dashboard:
5
+ chart: This value has changed
6
+ details: This key/value has been added
7
+ menu:
8
+ first: First menu item
9
+ second: Second menu item
10
+ not_in_es: This key not in spanish
@@ -0,0 +1,6 @@
1
+ es:
2
+ marketing:
3
+ banner: hola
4
+ dashboard:
5
+ chart: Es muy bonita
6
+ details: grande
@@ -0,0 +1,13 @@
1
+ ---
2
+ en:
3
+ models:
4
+ product:
5
+ id_125:
6
+ description: Green with megawatts
7
+ name: Lazer
8
+ id_36:
9
+ description: Polarized and lazer resistant
10
+ name: This nested value has changed
11
+ id_55:
12
+ description: A new nested description
13
+ name: a new nested name
@@ -0,0 +1,13 @@
1
+ ---
2
+ es:
3
+ models:
4
+ product:
5
+ id_125:
6
+ description: Verde
7
+ name: Lazero
8
+ id_36:
9
+ description: Negro
10
+ name: No Lazero
11
+ id_55:
12
+ description: Azul
13
+ name: Muy bonita
@@ -0,0 +1,21 @@
1
+ cn:
2
+ marketing:
3
+ banner: 這改變了營銷信息
4
+ dashboard:
5
+ chart: 此值已更改
6
+ details: 此鍵 /值已添加
7
+ menu:
8
+ first: 第一個菜單項
9
+ second: 第二個菜單項
10
+ models:
11
+ product:
12
+ id_125:
13
+ description: 綠色與兆瓦
14
+ name: 休閒
15
+ id_36:
16
+ description: 極化和激光耐
17
+ name: 此嵌套值已更改
18
+ id_55:
19
+ description: 一個新的嵌套描述
20
+ name: 一個新的嵌套的名稱
21
+ translator_cruft: 這是不小心添加了一個翻譯
@@ -0,0 +1,12 @@
1
+ es:
2
+ menu:
3
+ first: Uno
4
+ second: Dos
5
+ marketing:
6
+ banner: hola mi amigo
7
+ models:
8
+ product:
9
+ id_125:
10
+ description: mucho Verde
11
+ name: mucho Lazero
12
+ translator_cruft: estúpido
@@ -0,0 +1,95 @@
1
+ require "spec_helper"
2
+
3
+ describe "Vocab::Extractor::Android" do
4
+
5
+ before( :each ) do
6
+ @locale = "#{vocab_root}/spec/data/android/locales/values/strings.xml"
7
+ end
8
+
9
+ describe 'extract_current' do
10
+
11
+ it "extracts hash of current string translations" do
12
+ actual = Vocab::Extractor::Android.extract_current( @locale )
13
+ actual.should eql( { "app_name" =>"Kid Mode",
14
+ "delete" =>"Delete",
15
+ "cancel" =>"Cancel",
16
+ "app_current"=>"current",
17
+ "pd_app_name"=>"Parent Dashboard",
18
+ "not_in_es" =>"This key not in spanish",} )
19
+ end
20
+
21
+ end
22
+
23
+ describe 'extract_previous' do
24
+
25
+ before( :each ) do
26
+ Dir.chdir( vocab_root )
27
+ end
28
+
29
+ before( :each ) do
30
+ @sha = '2302e5acb50a9b56d113c46805de5ae0c12d28c0'
31
+ Vocab.settings.stub!( :last_translation ).and_return( @sha )
32
+ end
33
+
34
+ it "extracts hash of previous string translations" do
35
+ actual = Vocab::Extractor::Android.extract_previous( @locale )
36
+ actual.should eql( { 'app_name' => 'Kid Mode',
37
+ 'pd_app_name' => 'Parent Dashboard',
38
+ 'app_current' => 'current' } )
39
+ end
40
+
41
+ end
42
+
43
+ describe 'write_full' do
44
+
45
+ it 'writes the full translation to the correct xml file' do
46
+ hash = { 'foo' => 'bar' }
47
+ Vocab::Translator::Android.should_receive( :write ).
48
+ with( hash, "#{vocab_root}/strings.full.xml" )
49
+ Vocab::Extractor::Android.write_full( hash )
50
+ end
51
+
52
+ end
53
+
54
+ describe 'write_diff' do
55
+
56
+ it 'writes the diff translation to the correct xml file' do
57
+ hash = { 'foo' => 'bar' }
58
+ Vocab::Translator::Android.should_receive( :write ).
59
+ with( hash, "#{vocab_root}/strings.diff.xml" )
60
+ Vocab::Extractor::Android.write_diff( hash )
61
+ end
62
+
63
+ end
64
+
65
+ describe 'write' do
66
+
67
+ it 'writes the translation to a xml file' do
68
+ translation = { 'app_name' => 'Kid Mode',
69
+ 'pd_app_name' => 'Parent Dashboard',
70
+ 'delete' => "La funci&#xF3;n Child Lock" }
71
+ path = "#{vocab_root}/spec/tmp/strings.xml"
72
+ Vocab::Translator::Android.write( translation, path )
73
+ should_eql_file( File.open( path ) { |f| f.read },
74
+ "spec/data/android/write.xml" )
75
+ File.delete( path )
76
+ end
77
+
78
+ end
79
+
80
+ describe 'examples' do
81
+
82
+ it 'returns file names for completed translations' do
83
+ dir = "#{vocab_root}/spec/data/android/locales"
84
+ actual = Vocab::Extractor::Android.examples( dir )
85
+ actual.should =~ [ "tmp/translations/values-es" ]
86
+ end
87
+
88
+ it 'uses the conventional android locales directory by default' do
89
+ Vocab::Translator::Android.should_receive( :locales ).with( "#{vocab_root}/res/values" ).and_return( [] )
90
+ Vocab::Extractor::Android.examples
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,134 @@
1
+ require "spec_helper"
2
+
3
+ describe "Vocab::Extractor::Base" do
4
+
5
+ describe "diff" do
6
+
7
+ it "finds the new keys in the current hash" do
8
+ @current = { 1 => 2, 3 => 4 }
9
+ @previous = { 1 => 2 }
10
+ diff = Vocab::Extractor::Base.diff( @previous, @current )
11
+ diff.should == { 3 => 4 }
12
+ end
13
+
14
+ it "finds the updated keys in the current hash" do
15
+ @current = { 1 => 10 }
16
+ @previous = { 1 => 2 }
17
+ diff = Vocab::Extractor::Base.diff( @previous, @current )
18
+ diff.should == { 1 => 10 }
19
+ end
20
+
21
+ it "ignores updated keys that begin with 'debug_'" do
22
+ @current = { 'debug_foo' => 'bar' }
23
+ @previous = {}
24
+ diff = Vocab::Extractor::Base.diff( @previous, @current )
25
+ diff.should == {}
26
+ end
27
+
28
+ it "handles the hash format of the translations" do
29
+ @current = { :"en.marketing.banner"=>"This product is so good",
30
+ :"en.models.product.id_125.name"=>"This has been updated",
31
+ :"en.models.product.id_126.name"=>"This is new"}
32
+ @previous = { :"en.marketing.banner"=>"This product is so good",
33
+ :"en.models.product.id_125.name"=>"Lazer"}
34
+ diff = Vocab::Extractor::Base.diff( @previous, @current )
35
+ diff.should == { :"en.models.product.id_125.name"=>"This has been updated",
36
+ :"en.models.product.id_126.name"=>"This is new"}
37
+ end
38
+
39
+ end
40
+
41
+ describe "extract" do
42
+
43
+ before( :each ) do
44
+ @diff_path = "#{vocab_root}/spec/tmp/en.yml"
45
+ @full_path = "#{vocab_root}/spec/tmp/en.yml"
46
+ File.delete( @diff_path ) if File.exists?( @diff_path )
47
+ end
48
+
49
+ it "extracts the strings that need to be translated into a yml file" do
50
+ current = { 1 => 5, 3 => 4 }
51
+ previous = { 1 => 2 }
52
+ Vocab::Extractor::Base.should_receive( :extract_current ).and_return( current )
53
+ Vocab::Extractor::Base.should_receive( :extract_previous ).and_return( previous )
54
+ Vocab::Extractor::Base.should_receive( :write_diff ).with( { 1 => 5, 3 => 4 }, @diff_path )
55
+ Vocab::Extractor::Base.should_receive( :write_full ).with( current, @full_path )
56
+ Vocab::Extractor::Base.should_receive( :mkdir_examples )
57
+ Vocab::Extractor::Base.extract( @diff_path, @full_path )
58
+ end
59
+
60
+ end
61
+
62
+ describe "previous_file" do
63
+
64
+ before( :each ) do
65
+ Dir.chdir( vocab_root )
66
+ @path = "spec/data/android/locales/strings.xml"
67
+ end
68
+
69
+ it "returns the contents of a file from a specific git version" do
70
+ contents = Vocab::Extractor::Base.previous_file( @path, 'a19f7c5c28c1158792a966c0d2153a04490dd35e' )
71
+ should_eql_file( contents, 'spec/data/android/previous.xml' )
72
+
73
+ contents = Vocab::Extractor::Base.previous_file( @path, '0533bcd9a304cd6e74d6a56959dbcabd57b2f1b9' )
74
+ should_eql_file( contents, 'spec/data/android/current.xml' )
75
+ end
76
+
77
+ it "returns the correct contents when a full path is specified" do
78
+ contents = Vocab::Extractor::Base.previous_file( "#{vocab_root}/#{@path}", 'a19f7c5c28c1158792a966c0d2153a04490dd35e' )
79
+ should_eql_file( contents, 'spec/data/android/previous.xml' )
80
+ end
81
+
82
+ it "returns the contents of a file when in a subdir of the git directory" do
83
+ Dir.chdir( "spec" )
84
+ @path = "data/android/locales/strings.xml"
85
+ contents = Vocab::Extractor::Base.previous_file( @path, 'a19f7c5c28c1158792a966c0d2153a04490dd35e' )
86
+ should_eql_file( contents, 'spec/data/android/previous.xml' )
87
+ end
88
+
89
+ end
90
+
91
+ describe 'git_root' do
92
+
93
+ it 'returns empty string if already at git root' do
94
+ Dir.chdir( vocab_root )
95
+ Vocab::Extractor::Base.git_root.should eql( vocab_root )
96
+ end
97
+
98
+ it 'returns path to git root if in subdir' do
99
+ Dir.chdir( "#{vocab_root}/spec/data" )
100
+ Vocab::Extractor::Base.git_root.should eql( vocab_root )
101
+ Dir.chdir( "#{vocab_root}" )
102
+ end
103
+
104
+ end
105
+
106
+ describe 'git_relative_path' do
107
+
108
+ it 'returns the path to the specified file relative to the git root dir' do
109
+ Dir.chdir( vocab_root )
110
+ path = "spec/data/android/locales/values/strings.xml"
111
+ Vocab::Extractor::Base.git_path( path ).should eql( path )
112
+ end
113
+
114
+ it 'returns the path to the specified file relative to the git root dir while in subdir' do
115
+ Dir.chdir( "#{vocab_root}/spec/data" )
116
+ path = "android/locales/values/strings.xml"
117
+ git_path = "spec/data/android/locales/values/strings.xml"
118
+ Vocab::Extractor::Base.git_path( path ).should eql( git_path )
119
+ end
120
+
121
+ end
122
+
123
+ describe 'mkdir_examples' do
124
+
125
+ it 'creates example directories for completed translations' do
126
+ examples = [ 'tmp/translations/values-zh', 'tmp/translations/values-zh-rCN' ]
127
+ examples.each { |example| FileUtils.should_receive( :mkdir_p ).with( example ) }
128
+ Vocab::Extractor::Base.should_receive( :examples ).and_return( examples )
129
+ Vocab::Extractor::Base.mkdir_examples
130
+ end
131
+
132
+ end
133
+
134
+ end