vocab 0.0.1

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 (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