air18n 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.
- 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
|