air18n 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rvmrc +48 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +9 -0
- data/air18n.gemspec +25 -0
- data/lib/air18n.rb +83 -0
- data/lib/air18n/backend.rb +194 -0
- data/lib/air18n/class_methods.rb +292 -0
- data/lib/air18n/less_silly_chain.rb +54 -0
- data/lib/air18n/logging_helper.rb +13 -0
- data/lib/air18n/mock_priority.rb +25 -0
- data/lib/air18n/phrase.rb +92 -0
- data/lib/air18n/phrase_screenshot.rb +76 -0
- data/lib/air18n/phrase_translation.rb +348 -0
- data/lib/air18n/prim_and_proper.rb +94 -0
- data/lib/air18n/priority.rb +13 -0
- data/lib/air18n/pseudo_locales.rb +53 -0
- data/lib/air18n/reflection.rb +10 -0
- data/lib/air18n/screenshot.rb +45 -0
- data/lib/air18n/testing_support/factories.rb +3 -0
- data/lib/air18n/testing_support/factories/phrase.rb +8 -0
- data/lib/air18n/testing_support/factories/phrase_screenshot.rb +8 -0
- data/lib/air18n/testing_support/factories/phrase_translation.rb +17 -0
- data/lib/air18n/version.rb +3 -0
- data/lib/air18n/xss_detector.rb +47 -0
- data/lib/generators/air18n/migration/migration_generator.rb +39 -0
- data/lib/generators/air18n/migration/templates/active_record/migration.rb +63 -0
- data/spec/database.yml +3 -0
- data/spec/factories.rb +2 -0
- data/spec/lib/air18n/air18n_spec.rb +144 -0
- data/spec/lib/air18n/backend_spec.rb +173 -0
- data/spec/lib/air18n/phrase_translation_spec.rb +80 -0
- data/spec/lib/air18n/prim_and_proper_spec.rb +21 -0
- data/spec/lib/air18n/pseudo_locales_spec.rb +17 -0
- data/spec/lib/air18n/xss_detector_spec.rb +47 -0
- data/spec/spec_helper.rb +62 -0
- metadata +212 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'air18n/mock_priority'
|
4
|
+
|
5
|
+
describe Air18n::Backend do
|
6
|
+
|
7
|
+
before do
|
8
|
+
@scope = []
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'Translation auto-update' do
|
12
|
+
before do
|
13
|
+
@backend = Air18n::Backend.new
|
14
|
+
end
|
15
|
+
it 'Should have default override DB value' do
|
16
|
+
phrase = FactoryGirl.create(:phrase, :value => 'original_value')
|
17
|
+
@backend.lookup(:en, phrase.key, @scope, :default => 'new_value').should == 'new_value'
|
18
|
+
phrase.reload.value.should == 'new_value'
|
19
|
+
end
|
20
|
+
it 'Should have default override empty DB' do
|
21
|
+
@backend.lookup(:en, 'key', @scope, :default => 'brand_new_value').should == 'brand_new_value'
|
22
|
+
@backend.lookup(:en, 'key', @scope).should == 'brand_new_value'
|
23
|
+
end
|
24
|
+
it 'Should have low-priority default override empty DB' do
|
25
|
+
@backend.lookup(:en, 'key', @scope, :default => 'low_priority_brand_new_value', :default_is_low_priority => true).should == 'low_priority_brand_new_value'
|
26
|
+
@backend.lookup(:en, 'key', @scope).should == 'low_priority_brand_new_value'
|
27
|
+
end
|
28
|
+
it 'Should have low-priority default *not* override nonempty DB value' do
|
29
|
+
phrase = FactoryGirl.create(:phrase, :value => 'original_value')
|
30
|
+
@backend.init_translations(I18n.default_locale)
|
31
|
+
@backend.lookup(I18n.default_locale, phrase.key, @scope, :default => 'new_value', :default_is_low_priority => true).should == 'new_value'
|
32
|
+
phrase.reload.value.should == 'original_value'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'Robustness to various input formats' do
|
37
|
+
before do
|
38
|
+
@backend = Air18n::Backend.new
|
39
|
+
end
|
40
|
+
it 'should not barf on arrays' do
|
41
|
+
@backend.lookup(I18n.default_locale, ['january', 'february'], @scope).should == nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'I18n backend initialization' do
|
46
|
+
before :all do
|
47
|
+
@phrase1 = FactoryGirl.create(:phrase, :key => 'init, key 1', :value => 'value 1')
|
48
|
+
@phrase2 = FactoryGirl.create(:phrase, :key => 'init, key 2', :value => 'value 2')
|
49
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'French value 1', :locale => :fr)
|
50
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Spanish value 1', :locale => :es)
|
51
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :value => 'Spanish value 2', :locale => :es)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should lazily load all translations on startup' do
|
55
|
+
@backend = Air18n::Backend.new
|
56
|
+
@backend.available_locales.should be_empty
|
57
|
+
@backend.lookup(:es, @phrase1.key).should == 'Spanish value 1'
|
58
|
+
@backend.lookup(:fr, @phrase1.key).should == 'French value 1'
|
59
|
+
@backend.lookup(:es, @phrase2.key).should == 'Spanish value 2'
|
60
|
+
|
61
|
+
# This is undesirable behavior. Before any English lookup has been done,
|
62
|
+
# English translations aren't loaded.
|
63
|
+
@backend.lookup(:ko, @phrase1.key).should == nil
|
64
|
+
|
65
|
+
@backend.lookup(:en, @phrase1.key).should == 'value 1'
|
66
|
+
@backend.lookup(:ko, @phrase1.key).should == 'value 1'
|
67
|
+
@backend.lookup(:en, @phrase2.key).should == 'value 2'
|
68
|
+
@backend.available_locales.size.should == 3
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should load only still-used translations on startup' do
|
72
|
+
Air18n::MockPriority.mock_priority({ @phrase1.key => 1 }) do |mock_priority|
|
73
|
+
@backend = Air18n::Backend.new
|
74
|
+
@backend.lookup(:es, @phrase1.key, [], :default => @phrase1.value).should == 'Spanish value 1'
|
75
|
+
@backend.lookup(:es, @phrase2.key, [], :default => @phrase2.value).should == 'value 2'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'Pseudolocales' do
|
81
|
+
it 'should use pseudolocales for xx locale' do
|
82
|
+
@backend = Air18n::Backend.new
|
83
|
+
@backend.lookup(:xx, 'xx, key 1', @scope, :default => 'hey there').should == Air18n::PseudoLocales::translate(:xx, 'hey there')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'Language fallbacks' do
|
88
|
+
it 'should fall back to default locale' do
|
89
|
+
old_default_locale = I18n.default_locale
|
90
|
+
I18n.reset(:fr)
|
91
|
+
@backend = Air18n::Backend.new
|
92
|
+
@backend.lookup(:fr, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
|
93
|
+
@backend.lookup(:es, 'fallbacks, key 1', @scope, :default => 'merci').should == 'merci'
|
94
|
+
@backend.lookup(:es, 'fallbacks, key 1', @scope).should == 'merci'
|
95
|
+
@backend.lookup(:en, 'fallbacks, key 1', @scope).should == 'merci'
|
96
|
+
|
97
|
+
# Restore the old default locale.
|
98
|
+
I18n.reset(old_default_locale)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should fallback to less specific locale' do
|
102
|
+
@phrase1 = FactoryGirl.create(:phrase, :key => 'less specific fallbacks, key 1', :value => 'value 1')
|
103
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Spanish Spanish value 1', :locale => :es)
|
104
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :value => 'Latin American Spanish value 1', :locale => :"es-419")
|
105
|
+
@phrase2 = FactoryGirl.create(:phrase, :key => 'less specific fallbacks, key 2', :value => 'value 2')
|
106
|
+
FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :value => 'Spanish Spanish value 2', :locale => :es)
|
107
|
+
|
108
|
+
@backend = Air18n::Backend.new
|
109
|
+
@backend.lookup(:es, 'less specific fallbacks, key 1', @scope).should == 'Spanish Spanish value 1'
|
110
|
+
@backend.lookup(:"es-419", 'less specific fallbacks, key 1', @scope).should == 'Latin American Spanish value 1'
|
111
|
+
@backend.lookup(:es, 'less specific fallbacks, key 2', @scope).should == 'Spanish Spanish value 2'
|
112
|
+
@backend.lookup(:"es-419", 'less specific fallbacks, key 2', @scope).should == 'Spanish Spanish value 2'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'Language variants' do
|
117
|
+
before :all do
|
118
|
+
I18n.reset(:en)
|
119
|
+
@backend = Air18n::Backend.new
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should produce British English' do
|
123
|
+
@backend.lookup(:en, 'british english, key 1', @scope, :default => 'canceled neighborhood')
|
124
|
+
@backend.lookup(:"en-GB", 'british english, key 1', @scope, :default => 'value 1').should == 'cancelled neighbourhood'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'Keeping track of which phrases have screenshots' do
|
129
|
+
before do
|
130
|
+
@backend = Air18n::Backend.new
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should take one screenshot of a new page' do
|
134
|
+
I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
|
135
|
+
@backend.lookup(:en, 'brand new key', @scope, :default => 'sparkle', :routes_context => 'new_controller#new_page')
|
136
|
+
I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').should include({ 'brand new key' => 'sparkle' })
|
137
|
+
I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
|
138
|
+
@backend.lookup(:en, 'brand new key', @scope, :default => 'sparkle', :routes_context => 'new_controller#new_page')
|
139
|
+
I18n.retrieve_phrases_needing_screenshot('new_controller#new_page').size.should == 0
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'XSS attack detection' do
|
144
|
+
before do
|
145
|
+
@backend = Air18n::Backend.new
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should return nil upon new tags' do
|
149
|
+
@backend.lookup(:en, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>')
|
150
|
+
|
151
|
+
# Falls back to default when there is a missing tag.
|
152
|
+
@backend.store_translations(:es, { 'xss, key 1' => 'Too few tags' })
|
153
|
+
@backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
|
154
|
+
|
155
|
+
# Falls back to default when there is a missing tag.
|
156
|
+
@backend.store_translations(:es, { 'xss, key 1' => 'Too <a href="/here"><b>many</b></a> tags' })
|
157
|
+
@backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
|
158
|
+
|
159
|
+
# Translates nicely when tags match.
|
160
|
+
@backend.store_translations(:es, { 'xss, key 1' => 'Just <a href="/here">right</a> tags' })
|
161
|
+
@backend.lookup(:es, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Just <a href="/here">right</a> tags'
|
162
|
+
|
163
|
+
# There is no French translation, so the default translation should be used and allowed.
|
164
|
+
@backend.lookup(:fr, 'xss, key 1', @scope, :default => 'Click <a href="/here">here</a>').should == 'Click <a href="/here">here</a>'
|
165
|
+
|
166
|
+
# With :disable_xss_check, anything goes.
|
167
|
+
@backend.store_translations(:fr, { 'xss, key 2' => '<p>translated <b>text</b></p>' })
|
168
|
+
@backend.lookup(:fr, 'xss, key 2', @scope, :default => '<p>rich text</p>').should == '<p>rich text</p>'
|
169
|
+
@backend.lookup(:fr, 'xss, key 2', @scope, :default => '<p>rich text</p>', :disable_xss_check => true).should == '<p>translated <b>text</b></p>'
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Air18n::PhraseTranslation do
|
4
|
+
|
5
|
+
context 'latest flag' do
|
6
|
+
it "PhraseTranslation Should get unset when a new translation is made" do
|
7
|
+
original_translation = FactoryGirl.create(:phrase_translation)
|
8
|
+
original_translation.should be_latest
|
9
|
+
new_translation = FactoryGirl.create(:phrase_translation, :phrase => original_translation.phrase)
|
10
|
+
original_translation.reload.should_not be_latest
|
11
|
+
new_translation.should be_latest
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'PhraseTranslation stale flag' do
|
16
|
+
it "Should get set when a phrase is updated" do
|
17
|
+
translation = FactoryGirl.create(:phrase_translation)
|
18
|
+
translation.should_not be_stale
|
19
|
+
translation.phrase.value = "new value"
|
20
|
+
translation.phrase.save!
|
21
|
+
translation.reload.should be_stale
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'compute monthly translator payments' do
|
26
|
+
before do
|
27
|
+
# TODO(jason) Stub out User.
|
28
|
+
# @user1 = FactoryGirl.create(:user)
|
29
|
+
# @user2 = FactoryGirl.create(:user)
|
30
|
+
@phrase1 = FactoryGirl.create(:phrase, :value => 'two words')
|
31
|
+
@phrase2 = FactoryGirl.create(:phrase, :value => 'three long words')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should pay for only last month's translation" do
|
35
|
+
pending "Stub out User class"
|
36
|
+
without_timestamping_of Air18n::PhraseTranslation do
|
37
|
+
translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 1.month.ago)
|
38
|
+
other_phrase_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user1, :created_at => 1.month.ago)
|
39
|
+
other_user_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user2, :created_at => 1.month.ago)
|
40
|
+
older_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 2.months.ago)
|
41
|
+
|
42
|
+
data = Air18n::PhraseTranslation.translator_payment_data
|
43
|
+
|
44
|
+
data[0].should include(
|
45
|
+
{:locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 1, :smart_name => @user1.smart_name, :user_id => @user1.id, :month => 1.month.ago.month, :is_admin => false, :year => 1.month.ago.year })
|
46
|
+
data[0][:activity].should include(
|
47
|
+
{ :payment => 5 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 2, :num_phrases_prev_translated => 1, :word_count_translations => 5 })
|
48
|
+
|
49
|
+
data[1].should include(
|
50
|
+
{ :locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 0, :smart_name => @user2.smart_name, :user_id => @user2.id, :is_admin => false })
|
51
|
+
data[1][:activity].should include(
|
52
|
+
{ :payment => 2 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 1, :num_phrases_prev_translated => 0, :word_count_translations => 2 })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should pay less for verification' do
|
57
|
+
pending "Stub out User class"
|
58
|
+
without_timestamping_of Air18n::PhraseTranslation do
|
59
|
+
translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user1, :created_at => 1.month.ago)
|
60
|
+
other_phrase_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user1, :created_at => 1.month.ago)
|
61
|
+
other_user_translation = FactoryGirl.create(:phrase_translation, :phrase => @phrase1, :user => @user2, :created_at => 1.month.ago)
|
62
|
+
phrase_verification = FactoryGirl.create(:phrase_translation, :phrase => @phrase2, :user => @user2, :is_verification => true, :created_at => 1.month.ago)
|
63
|
+
|
64
|
+
|
65
|
+
data = Air18n::PhraseTranslation.translator_payment_data
|
66
|
+
|
67
|
+
data[0].should include(
|
68
|
+
{:locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 1, :smart_name => @user1.smart_name, :user_id => @user1.id, :month => 1.month.ago.month, :is_admin => false, :year => 1.month.ago.year })
|
69
|
+
data[0][:activity].should include(
|
70
|
+
{ :payment => 5 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION, :num_translations => 2, :word_count_translations => 5 })
|
71
|
+
|
72
|
+
data[1].should include(
|
73
|
+
{ :locale => "es", :is_paid => true, :currency => "EUR", :usd_amount => 0, :smart_name => @user2.smart_name, :user_id => @user2.id, :is_admin => false })
|
74
|
+
data[1][:activity].should include(
|
75
|
+
{ :payment => 2 * Air18n::PhraseTranslation::COST_PER_WORD_TRANSLATION + 3 * Air18n::PhraseTranslation::COST_PER_WORD_VERIFICATION, :num_translations => 1, :num_verifications => 1, :word_count_translations => 2, :word_count_verifications => 3 })
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Air18n::PrimAndProper do
|
4
|
+
describe "AmericanToBritish" do
|
5
|
+
before do
|
6
|
+
@airpnp = Air18n::PrimAndProper.new
|
7
|
+
end
|
8
|
+
it "should handle simple replacement" do
|
9
|
+
@airpnp.guess('flavored inquiries', :en, :"en-GB").should == 'flavoured enquiries'
|
10
|
+
end
|
11
|
+
it "should handle capitals and punctuation" do
|
12
|
+
@airpnp.guess('Flavored inquiries.', :en, :"en-GB").should == 'Flavoured enquiries.'
|
13
|
+
end
|
14
|
+
it "should handle quotes" do
|
15
|
+
@airpnp.guess('Click "canceled" for canceling.', :en, :"en-GB").should == 'Click "cancelled" for cancelling.'
|
16
|
+
end
|
17
|
+
it "should handle vocab variants and article adaption" do
|
18
|
+
@airpnp.guess('"An apartment has an elevator with the couch."', :en, :"en-GB").should == '"A flat has a lift with the sofa."'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Air18n::PseudoLocales do
|
4
|
+
context 'xx' do
|
5
|
+
it 'should work' do
|
6
|
+
Air18n::PseudoLocales.translate(:xx, 'here be %{num_pirates}, pirates.').should == '[xxxx xx %{num_pirates}, xxxxxxx.]'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'xx-long' do
|
11
|
+
it 'should work' do
|
12
|
+
Air18n::PseudoLocales.translate(:xxlong, 'here be %{num_pirates}, pirates.').should == 'here be %{num_pirates}, pirates. one two three four five six seven'
|
13
|
+
Air18n::PseudoLocales.translate(:xxlong, 'hey there').should == 'hey there one two'
|
14
|
+
Air18n::PseudoLocales.translate(:xxlong, 'h').should == 'h one'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'air18n/xss_detector'
|
4
|
+
|
5
|
+
describe Air18n::XssDetector do
|
6
|
+
describe "has_dubious_escape_characters?" do
|
7
|
+
it "should detect backslashes" do
|
8
|
+
Air18n::XssDetector::has_dubious_escape_characters?("Let's escape a \\' there").should == true
|
9
|
+
Air18n::XssDetector::has_dubious_escape_characters?("All's clear").should == false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "safe?" do
|
14
|
+
it "should detect safe texts" do
|
15
|
+
Air18n::XssDetector::safe?("safe", "safe").should == { :safe => true }
|
16
|
+
Air18n::XssDetector::safe?("<tag>", "<tag>").should == { :safe => true }
|
17
|
+
Air18n::XssDetector::safe?("safe", { "safe" => "whatevs" }).should == { :safe => true }
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should detect dubious escape characters" do
|
21
|
+
Air18n::XssDetector::safe?("Let's escape a \\' there", "safe").should == { :safe => false, :reason => "Backslashes are not allowed" }
|
22
|
+
Air18n::XssDetector::safe?("safe", "Let's escape a \\' there").should == { :safe => false, :reason => "Backslashes are not allowed" }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should detect tag mismatches" do
|
26
|
+
Air18n::XssDetector::safe?("<tag>", "safe").should == { :safe => false, :reason => "HTML tags don't match: #{['<tag>'].inspect} vs #{[].inspect}" }
|
27
|
+
Air18n::XssDetector::safe?("safe", "<tag>").should == { :safe => false, :reason => "HTML tags don't match: #{[].inspect} vs #{['<tag>'].inspect}" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "extract_tags" do
|
32
|
+
def test(text, tags)
|
33
|
+
Air18n::XssDetector::extract_tags(text).should == tags
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should work" do
|
37
|
+
test("oh boy!<script sup>hello!</script> oh boy!", ["<script sup>", "</script>"])
|
38
|
+
test("oh boy!<<script sup>hello!</script> oh boy!", ["<<script sup>hello!</script> oh boy!"])
|
39
|
+
test('Success! You\'ve invited %{number} friends. <a href="/referrals">Invite <b>more</b>.</a>', ['<a href="/referrals">', '<b>', '</b>', '</a>'])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'Robustness to various input formats' do
|
44
|
+
it 'should not barf on arrays' do
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
$LOAD_PATH << "." unless $LOAD_PATH.include?(".")
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "rubygems"
|
6
|
+
require "bundler"
|
7
|
+
|
8
|
+
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.5")
|
9
|
+
raise RuntimeError, "Your bundler version is too old." +
|
10
|
+
"Run `gem install bundler` to upgrade."
|
11
|
+
end
|
12
|
+
|
13
|
+
# Set up load paths for all bundled gems
|
14
|
+
Bundler.setup
|
15
|
+
rescue Bundler::GemNotFound
|
16
|
+
raise RuntimeError, "Bundler couldn't find some gems." +
|
17
|
+
"Did you run \`bundle install\`?"
|
18
|
+
end
|
19
|
+
|
20
|
+
require "active_record"
|
21
|
+
|
22
|
+
# set adapter to use, default is sqlite3
|
23
|
+
# to use an alternative adapter run => rake spec DB='postgresql'
|
24
|
+
db_name = ENV['DB'] || 'sqlite3'
|
25
|
+
database_yml = File.expand_path('../database.yml', __FILE__)
|
26
|
+
|
27
|
+
if File.exists?(database_yml)
|
28
|
+
active_record_configuration = YAML.load_file(database_yml)
|
29
|
+
|
30
|
+
ActiveRecord::Base.configurations = active_record_configuration
|
31
|
+
config = ActiveRecord::Base.configurations[db_name]
|
32
|
+
|
33
|
+
ActiveRecord::Base.establish_connection(db_name)
|
34
|
+
ActiveRecord::Base.connection
|
35
|
+
|
36
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
|
37
|
+
ActiveRecord::Base.default_timezone = :utc
|
38
|
+
|
39
|
+
ActiveRecord::Base.silence do
|
40
|
+
ActiveRecord::Migration.verbose = false
|
41
|
+
|
42
|
+
require 'generators/air18n/migration/templates/active_record/migration'
|
43
|
+
Air18nMigration.up
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise "Please create #{database_yml} first to configure your database."
|
47
|
+
end
|
48
|
+
|
49
|
+
Bundler.require
|
50
|
+
require File.expand_path('../../lib/air18n', __FILE__)
|
51
|
+
|
52
|
+
# Why doesn't Bundler.require accomplish the loading of factory_girl?
|
53
|
+
require "factory_girl"
|
54
|
+
require 'air18n/testing_support/factories'
|
55
|
+
|
56
|
+
def clean_database!
|
57
|
+
Air18n::Reflection::MODELS.each do |model|
|
58
|
+
ActiveRecord::Base.connection.execute "DELETE FROM #{model.table_name}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
clean_database!
|
metadata
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: air18n
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jason Katz-Brown
|
9
|
+
- Nick Grandy
|
10
|
+
- Naseem Hakim
|
11
|
+
- Horace Ko
|
12
|
+
- Matt Baker
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
date: 2012-05-08 00:00:00.000000000 Z
|
17
|
+
dependencies:
|
18
|
+
- !ruby/object:Gem::Dependency
|
19
|
+
name: i18n
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
none: false
|
22
|
+
requirements:
|
23
|
+
- - ! '>='
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 0.5.0
|
26
|
+
type: :runtime
|
27
|
+
prerelease: false
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.5.0
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: activerecord
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3.0'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ~>
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '3.0'
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: rspec
|
52
|
+
requirement: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
type: :development
|
59
|
+
prerelease: false
|
60
|
+
version_requirements: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: sqlite3
|
68
|
+
requirement: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: guard
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: guard-rspec
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: factory_girl
|
116
|
+
requirement: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ~>
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '3.0'
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ~>
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '3.0'
|
130
|
+
description: Rails plugin with dyanmic I18n backend for amazing internationalization.
|
131
|
+
email:
|
132
|
+
- jason@airbnb.com
|
133
|
+
- nick@airbnb.com
|
134
|
+
- naseem@airbnb.com
|
135
|
+
- horace@airbnb.com
|
136
|
+
- matt.baker@airbnb.com
|
137
|
+
executables: []
|
138
|
+
extensions: []
|
139
|
+
extra_rdoc_files: []
|
140
|
+
files:
|
141
|
+
- .gitignore
|
142
|
+
- .rvmrc
|
143
|
+
- Gemfile
|
144
|
+
- LICENSE
|
145
|
+
- README.md
|
146
|
+
- Rakefile
|
147
|
+
- air18n.gemspec
|
148
|
+
- lib/air18n.rb
|
149
|
+
- lib/air18n/backend.rb
|
150
|
+
- lib/air18n/class_methods.rb
|
151
|
+
- lib/air18n/less_silly_chain.rb
|
152
|
+
- lib/air18n/logging_helper.rb
|
153
|
+
- lib/air18n/mock_priority.rb
|
154
|
+
- lib/air18n/phrase.rb
|
155
|
+
- lib/air18n/phrase_screenshot.rb
|
156
|
+
- lib/air18n/phrase_translation.rb
|
157
|
+
- lib/air18n/prim_and_proper.rb
|
158
|
+
- lib/air18n/priority.rb
|
159
|
+
- lib/air18n/pseudo_locales.rb
|
160
|
+
- lib/air18n/reflection.rb
|
161
|
+
- lib/air18n/screenshot.rb
|
162
|
+
- lib/air18n/testing_support/factories.rb
|
163
|
+
- lib/air18n/testing_support/factories/phrase.rb
|
164
|
+
- lib/air18n/testing_support/factories/phrase_screenshot.rb
|
165
|
+
- lib/air18n/testing_support/factories/phrase_translation.rb
|
166
|
+
- lib/air18n/version.rb
|
167
|
+
- lib/air18n/xss_detector.rb
|
168
|
+
- lib/generators/air18n/migration/migration_generator.rb
|
169
|
+
- lib/generators/air18n/migration/templates/active_record/migration.rb
|
170
|
+
- spec/database.yml
|
171
|
+
- spec/factories.rb
|
172
|
+
- spec/lib/air18n/air18n_spec.rb
|
173
|
+
- spec/lib/air18n/backend_spec.rb
|
174
|
+
- spec/lib/air18n/phrase_translation_spec.rb
|
175
|
+
- spec/lib/air18n/prim_and_proper_spec.rb
|
176
|
+
- spec/lib/air18n/pseudo_locales_spec.rb
|
177
|
+
- spec/lib/air18n/xss_detector_spec.rb
|
178
|
+
- spec/spec_helper.rb
|
179
|
+
homepage: http://www.github.com/airbnb/air18n
|
180
|
+
licenses: []
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
none: false
|
187
|
+
requirements:
|
188
|
+
- - ! '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
|
+
none: false
|
193
|
+
requirements:
|
194
|
+
- - ! '>='
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
requirements: []
|
198
|
+
rubyforge_project:
|
199
|
+
rubygems_version: 1.8.22
|
200
|
+
signing_key:
|
201
|
+
specification_version: 3
|
202
|
+
summary: Dynamic I18n backend
|
203
|
+
test_files:
|
204
|
+
- spec/database.yml
|
205
|
+
- spec/factories.rb
|
206
|
+
- spec/lib/air18n/air18n_spec.rb
|
207
|
+
- spec/lib/air18n/backend_spec.rb
|
208
|
+
- spec/lib/air18n/phrase_translation_spec.rb
|
209
|
+
- spec/lib/air18n/prim_and_proper_spec.rb
|
210
|
+
- spec/lib/air18n/pseudo_locales_spec.rb
|
211
|
+
- spec/lib/air18n/xss_detector_spec.rb
|
212
|
+
- spec/spec_helper.rb
|