globalize2 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +21 -0
  3. data/README.textile +202 -0
  4. data/Rakefile +39 -0
  5. data/VERSION +1 -0
  6. data/generators/db_backend.rb +0 -0
  7. data/generators/templates/db_backend_migration.rb +25 -0
  8. data/globalize2.gemspec +100 -0
  9. data/init.rb +8 -0
  10. data/lib/globalize/backend/chain.rb +102 -0
  11. data/lib/globalize/backend/pluralizing.rb +37 -0
  12. data/lib/globalize/backend/static.rb +61 -0
  13. data/lib/globalize/i18n/missing_translations_log_handler.rb +41 -0
  14. data/lib/globalize/i18n/missing_translations_raise_handler.rb +27 -0
  15. data/lib/globalize/load_path.rb +63 -0
  16. data/lib/globalize/locale/fallbacks.rb +63 -0
  17. data/lib/globalize/locale/language_tag.rb +81 -0
  18. data/lib/globalize/model/active_record.rb +56 -0
  19. data/lib/globalize/model/active_record/adapter.rb +100 -0
  20. data/lib/globalize/model/active_record/translated.rb +174 -0
  21. data/lib/globalize/translation.rb +32 -0
  22. data/lib/locale/root.yml +3 -0
  23. data/lib/rails_edge_load_path_patch.rb +40 -0
  24. data/notes.textile +51 -0
  25. data/test/all.rb +2 -0
  26. data/test/backends/chained_test.rb +175 -0
  27. data/test/backends/pluralizing_test.rb +63 -0
  28. data/test/backends/static_test.rb +147 -0
  29. data/test/data/locale/all.yml +2 -0
  30. data/test/data/locale/de-DE.yml +2 -0
  31. data/test/data/locale/en-US.yml +2 -0
  32. data/test/data/locale/en-US/module.yml +2 -0
  33. data/test/data/locale/fi-FI/module.yml +2 -0
  34. data/test/data/locale/root.yml +0 -0
  35. data/test/data/models.rb +40 -0
  36. data/test/data/no_globalize_schema.rb +11 -0
  37. data/test/data/schema.rb +39 -0
  38. data/test/i18n/missing_translations_test.rb +36 -0
  39. data/test/load_path_test.rb +49 -0
  40. data/test/locale/fallbacks_test.rb +154 -0
  41. data/test/locale/language_tag_test.rb +130 -0
  42. data/test/model/active_record/migration_test.rb +123 -0
  43. data/test/model/active_record/sti_translated_test.rb +75 -0
  44. data/test/model/active_record/translated_test.rb +487 -0
  45. data/test/test_helper.rb +36 -0
  46. data/test/translation_test.rb +54 -0
  47. metadata +116 -0
@@ -0,0 +1,130 @@
1
+ require File.join( File.dirname(__FILE__), '..', 'test_helper' )
2
+ require 'globalize/locale/language_tag'
3
+
4
+ include Globalize::Locale
5
+
6
+ class LanguageTagTest < ActiveSupport::TestCase
7
+ test "given a valid tag 'de' returns an LanguageTag from #tag" do
8
+ assert_instance_of LanguageTag, LanguageTag.tag('de')
9
+ end
10
+
11
+ test "given a valid tag 'de' returns an array of subtags" do
12
+ assert_equal ['de', nil, nil, nil, nil, nil, nil], LanguageTag::SimpleParser.match('de')
13
+ end
14
+
15
+ test "given a valid tag 'de-DE' returns an array of subtags" do
16
+ assert_equal ['de', nil, 'DE', nil, nil, nil, nil], LanguageTag::SimpleParser.match('de-DE')
17
+ end
18
+
19
+ test "given a valid lowercase tag 'de-latn-de-variant-x-phonebk' returns an array of subtags" do
20
+ assert_equal ['de', 'latn', 'de', 'variant', nil, 'x-phonebk', nil],
21
+ LanguageTag::SimpleParser.match('de-latn-de-variant-x-phonebk')
22
+ end
23
+
24
+ test "given a valid uppercase tag 'DE-LATN-DE-VARIANT-X-PHONEBK' returns an array of subtags" do
25
+ assert_equal ['DE', 'LATN', 'DE', 'VARIANT', nil, 'X-PHONEBK', nil],
26
+ LanguageTag::SimpleParser.match('DE-LATN-DE-VARIANT-X-PHONEBK')
27
+ end
28
+
29
+ test "given an invalid tag 'a-DE' test returns false" do
30
+ assert !LanguageTag::SimpleParser.match('a-DE')
31
+ end
32
+
33
+ test "given an invalid tag 'de-419-DE' test returns false" do
34
+ assert !LanguageTag::SimpleParser.match('de-419-DE')
35
+ end
36
+ end
37
+
38
+ class DeLatnLanguageTagTest < ActiveSupport::TestCase
39
+ def setup
40
+ subtags = %w(de Latn DE variant a-ext x-phonebk i-klingon)
41
+ @tag = LanguageTag.new *subtags
42
+ end
43
+
44
+ test "returns 'de' as the language subtag in lowercase" do
45
+ assert_equal 'de', @tag.language
46
+ end
47
+
48
+ test "returns 'Latn' as the script subtag in titlecase" do
49
+ assert_equal 'Latn', @tag.script
50
+ end
51
+
52
+ test "returns 'DE' as the region subtag in uppercase" do
53
+ assert_equal 'DE', @tag.region
54
+ end
55
+
56
+ test "returns 'variant' as the variant subtag in lowercase" do
57
+ assert_equal 'variant', @tag.variant
58
+ end
59
+
60
+ test "returns 'a-ext' as the extension subtag" do
61
+ assert_equal 'a-ext', @tag.extension
62
+ end
63
+
64
+ test "returns 'x-phonebk' as the privateuse subtag" do
65
+ assert_equal 'x-phonebk', @tag.privateuse
66
+ end
67
+
68
+ test "returns 'i-klingon' as the grandfathered subtag" do
69
+ assert_equal 'i-klingon', @tag.grandfathered
70
+ end
71
+
72
+ test "returns a formatted tag string from #to_s" do
73
+ assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon', @tag.to_s
74
+ end
75
+
76
+ test "returns an array containing the formatted subtags from #to_a" do
77
+ assert_equal %w(de Latn DE variant a-ext x-phonebk i-klingon), @tag.to_a
78
+ end
79
+ end
80
+
81
+ class InheritanceLanguageTagTest < ActiveSupport::TestCase
82
+ test "returns 'de-Latn-DE-variant-a-ext-x-phonebk' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
83
+ tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk i-klingon)
84
+ assert_equal 'de-Latn-DE-variant-a-ext-x-phonebk', tag.parent.to_s
85
+ end
86
+
87
+ test "returns 'de-Latn-DE-variant-a-ext' as the parent of 'de-Latn-DE-variant-a-ext-x-phonebk'" do
88
+ tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk)
89
+ assert_equal 'de-Latn-DE-variant-a-ext', tag.parent.to_s
90
+ end
91
+
92
+ test "returns 'de-Latn-DE-variant' as the parent of 'de-Latn-DE-variant-a-ext'" do
93
+ tag = LanguageTag.new *%w(de Latn DE variant a-ext)
94
+ assert_equal 'de-Latn-DE-variant', tag.parent.to_s
95
+ end
96
+
97
+ test "returns 'de-Latn-DE' as the parent of 'de-Latn-DE-variant'" do
98
+ tag = LanguageTag.new *%w(de Latn DE variant)
99
+ assert_equal 'de-Latn-DE', tag.parent.to_s
100
+ end
101
+
102
+ test "returns 'de-Latn' as the parent of 'de-Latn-DE'" do
103
+ tag = LanguageTag.new *%w(de Latn DE)
104
+ assert_equal 'de-Latn', tag.parent.to_s
105
+ end
106
+
107
+ test "returns 'de' as the parent of 'de-Latn'" do
108
+ tag = LanguageTag.new *%w(de Latn)
109
+ assert_equal 'de', tag.parent.to_s
110
+ end
111
+
112
+ # TODO RFC4647 says: "If no language tag matches the request, the "default" value is returned."
113
+ # where should we set the default language?
114
+ # test "returns '' as the parent of 'de'" do
115
+ # tag = LanguageTag.new *%w(de)
116
+ # assert_equal '', tag.parent.to_s
117
+ # end
118
+
119
+ test "returns an array of 5 parents for 'de-Latn-DE-variant-a-ext-x-phonebk-i-klingon'" do
120
+ parents = %w(de-Latn-DE-variant-a-ext-x-phonebk-i-klingon
121
+ de-Latn-DE-variant-a-ext-x-phonebk
122
+ de-Latn-DE-variant-a-ext
123
+ de-Latn-DE-variant
124
+ de-Latn-DE
125
+ de-Latn
126
+ de)
127
+ tag = LanguageTag.new *%w(de Latn DE variant a-ext x-phonebk i-klingon)
128
+ assert_equal parents, tag.parents.map{|tag| tag.to_s}
129
+ end
130
+ end
@@ -0,0 +1,123 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
2
+ require 'active_record'
3
+ require 'globalize/model/active_record'
4
+
5
+ # Hook up model translation
6
+ ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
7
+
8
+ # Load Post model
9
+ require File.join( File.dirname(__FILE__), '..', '..', 'data', 'models' )
10
+
11
+ class MigrationTest < ActiveSupport::TestCase
12
+ def setup
13
+ reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'no_globalize_schema.rb'))
14
+ end
15
+
16
+ test 'globalize table added' do
17
+ assert !Post.connection.table_exists?( :post_translations )
18
+ assert !Post.connection.index_exists?( :post_translations, :post_id )
19
+ Post.create_translation_table! :subject => :string, :content => :text
20
+ assert Post.connection.table_exists?( :post_translations )
21
+ assert Post.connection.index_exists?( :post_translations, :post_id )
22
+ columns = Post.connection.columns( :post_translations )
23
+ assert locale = columns.detect {|c| c.name == 'locale' }
24
+ assert_equal :string, locale.type
25
+ assert subject = columns.detect {|c| c.name == 'subject' }
26
+ assert_equal :string, subject.type
27
+ assert content = columns.detect {|c| c.name == 'content' }
28
+ assert_equal :text, content.type
29
+ assert post_id = columns.detect {|c| c.name == 'post_id' }
30
+ assert_equal :integer, post_id.type
31
+ assert created_at = columns.detect {|c| c.name == 'created_at' }
32
+ assert_equal :datetime, created_at.type
33
+ assert updated_at = columns.detect {|c| c.name == 'updated_at' }
34
+ assert_equal :datetime, updated_at.type
35
+ end
36
+
37
+ test 'globalize table dropped' do
38
+ assert !Post.connection.table_exists?( :post_translations )
39
+ assert !Post.connection.index_exists?( :post_translations, :post_id )
40
+ Post.create_translation_table! :subject => :string, :content => :text
41
+ assert Post.connection.table_exists?( :post_translations )
42
+ assert Post.connection.index_exists?( :post_translations, :post_id )
43
+ Post.drop_translation_table!
44
+ assert !Post.connection.table_exists?( :post_translations )
45
+ assert !Post.connection.index_exists?( :post_translations, :post_id )
46
+ end
47
+
48
+ test 'exception on missing field inputs' do
49
+ assert_raise Globalize::Model::MigrationMissingTranslatedField do
50
+ Post.create_translation_table! :content => :text
51
+ end
52
+ end
53
+
54
+ test 'exception on bad input type' do
55
+ assert_raise Globalize::Model::BadMigrationFieldType do
56
+ Post.create_translation_table! :subject => :string, :content => :integer
57
+ end
58
+ end
59
+
60
+ test "exception on bad input type isn't raised for untranslated fields" do
61
+ assert_nothing_raised do
62
+ Post.create_translation_table! :subject => :string, :content => :string, :views_count => :integer
63
+ end
64
+ end
65
+
66
+ test 'create_translation_table! should not be called on non-translated models' do
67
+ assert_raise NoMethodError do
68
+ Blog.create_translation_table! :name => :string
69
+ end
70
+ end
71
+
72
+ test 'drop_translation_table! should not be called on non-translated models' do
73
+ assert_raise NoMethodError do
74
+ Blog.drop_translation_table!
75
+ end
76
+ end
77
+
78
+ test "translation_index_name returns a readable index name when it's not longer than 50 characters" do
79
+ assert_equal 'index_post_translations_on_post_id', Post.send(:translation_index_name)
80
+ end
81
+
82
+ test "translation_index_name returns a hashed index name when it's longer than 50 characters" do
83
+ class UltraLongModelNameWithoutProper < ActiveRecord::Base
84
+ translates :foo
85
+ end
86
+ name = UltraLongModelNameWithoutProper.send(:translation_index_name)
87
+ assert_match /^index_[a-z0-9]{40}$/, name
88
+ end
89
+
90
+ test 'globalize table added when table has long name' do
91
+ UltraLongModelNameWithoutProper.create_translation_table!(
92
+ :subject => :string, :content => :text
93
+ )
94
+
95
+ assert UltraLongModelNameWithoutProper.connection.table_exists?(
96
+ :ultra_long_model_name_without_proper_translations
97
+ )
98
+ assert UltraLongModelNameWithoutProper.connection.index_exists?(
99
+ :ultra_long_model_name_without_proper_translations,
100
+ :name => UltraLongModelNameWithoutProper.send(
101
+ :translation_index_name
102
+ )
103
+ )
104
+ end
105
+
106
+ test 'globalize table dropped when table has long name' do
107
+ UltraLongModelNameWithoutProper.create_translation_table!(
108
+ :subject => :string, :content => :text
109
+ )
110
+
111
+ UltraLongModelNameWithoutProper.drop_translation_table!
112
+
113
+ assert !UltraLongModelNameWithoutProper.connection.table_exists?(
114
+ :ultra_long_model_name_without_proper_translations
115
+ )
116
+
117
+ assert !UltraLongModelNameWithoutProper.connection.index_exists?(
118
+ :ultra_long_model_name_without_proper_translations,
119
+ :ultra_long_model_name_without_proper_id
120
+ )
121
+ end
122
+
123
+ end
@@ -0,0 +1,75 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
2
+ require 'active_record'
3
+ require 'globalize/model/active_record'
4
+
5
+ # Hook up model translation
6
+ ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
7
+
8
+ # Load Post model
9
+ require File.join( File.dirname(__FILE__), '..', '..', 'data', 'models' )
10
+
11
+ class StiTranslatedTest < ActiveSupport::TestCase
12
+ def setup
13
+ I18n.locale = :'en-US'
14
+ I18n.fallbacks.clear
15
+ reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'schema.rb'))
16
+ end
17
+
18
+ def teardown
19
+ I18n.fallbacks.clear
20
+ end
21
+
22
+ test "works with simple dynamic finders" do
23
+ foo = Child.create :content => 'foo'
24
+ Child.create :content => 'bar'
25
+ child = Child.find_by_content('foo')
26
+ assert_equal foo, child
27
+ end
28
+
29
+ test 'change attribute on globalized model' do
30
+ child = Child.create :content => 'foo'
31
+ assert_equal [], child.changed
32
+ child.content = 'bar'
33
+ assert_equal [ 'content' ], child.changed
34
+ child.content = 'baz'
35
+ assert_member 'content', child.changed
36
+ end
37
+
38
+ test 'change attribute on globalized model after locale switching' do
39
+ child = Child.create :content => 'foo'
40
+ assert_equal [], child.changed
41
+ child.content = 'bar'
42
+ I18n.locale = :de
43
+ assert_equal [ 'content' ], child.changed
44
+ end
45
+
46
+ test 'fallbacks with lots of locale switching' do
47
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
48
+ child = Child.create :content => 'foo'
49
+
50
+ I18n.locale = :'de-DE'
51
+ assert_equal 'foo', child.content
52
+
53
+ I18n.locale = :'en-US'
54
+ child.update_attribute :content, 'bar'
55
+
56
+ I18n.locale = :'de-DE'
57
+ assert_equal 'bar', child.content
58
+ end
59
+
60
+ test "saves all locales, even after locale switching" do
61
+ child = Child.new :content => 'foo'
62
+ I18n.locale = 'de-DE'
63
+ child.content = 'bar'
64
+ I18n.locale = 'he-IL'
65
+ child.content = 'baz'
66
+ child.save
67
+ I18n.locale = 'en-US'
68
+ child = Child.first
69
+ assert_equal 'foo', child.content
70
+ I18n.locale = 'de-DE'
71
+ assert_equal 'bar', child.content
72
+ I18n.locale = 'he-IL'
73
+ assert_equal 'baz', child.content
74
+ end
75
+ end
@@ -0,0 +1,487 @@
1
+ require File.join( File.dirname(__FILE__), '..', '..', 'test_helper' )
2
+ require 'active_record'
3
+ require 'globalize/model/active_record'
4
+
5
+ # Hook up model translation
6
+ ActiveRecord::Base.send(:include, Globalize::Model::ActiveRecord::Translated)
7
+
8
+ # Load Post model
9
+ require File.join( File.dirname(__FILE__), '..', '..', 'data', 'models' )
10
+
11
+ class TranslatedTest < ActiveSupport::TestCase
12
+ def setup
13
+ I18n.locale = :'en-US'
14
+ I18n.fallbacks.clear
15
+ reset_db! File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'schema.rb'))
16
+ ActiveRecord::Base.locale = nil
17
+ end
18
+
19
+ def teardown
20
+ I18n.fallbacks.clear
21
+ end
22
+
23
+ test "modifiying translated fields" do
24
+ post = Post.create :subject => 'foo'
25
+ assert_equal 'foo', post.subject
26
+ post.subject = 'bar'
27
+ assert_equal 'bar', post.subject
28
+ end
29
+
30
+ test "modifiying translated fields while switching locales" do
31
+ post = Post.create :subject => 'foo'
32
+ assert_equal 'foo', post.subject
33
+ I18n.locale = :'de-DE'
34
+ post.subject = 'bar'
35
+ assert_equal 'bar', post.subject
36
+ I18n.locale = :'en-US'
37
+ assert_equal 'foo', post.subject
38
+ I18n.locale = :'de-DE'
39
+ post.subject = 'bar'
40
+ end
41
+
42
+ test "has post_translations" do
43
+ post = Post.create
44
+ assert_nothing_raised { post.globalize_translations }
45
+ end
46
+
47
+ test "has German post_translations" do
48
+ I18n.locale = :de
49
+ post = Post.create :subject => 'foo'
50
+ assert_equal 1, post.globalize_translations.size
51
+ I18n.locale = :en
52
+ assert_equal 1, post.globalize_translations.size
53
+ end
54
+
55
+ test "returns the value passed to :subject" do
56
+ post = Post.new
57
+ assert_equal 'foo', (post.subject = 'foo')
58
+ end
59
+
60
+ test "translates subject and content into en-US" do
61
+ post = Post.create :subject => 'foo', :content => 'bar'
62
+ assert_equal 'foo', post.subject
63
+ assert_equal 'bar', post.content
64
+ assert post.save
65
+ post.reload
66
+ assert_equal 'foo', post.subject
67
+ assert_equal 'bar', post.content
68
+ end
69
+
70
+ test "finds a German post" do
71
+ post = Post.create :subject => 'foo (en)', :content => 'bar'
72
+ I18n.locale = 'de-DE'
73
+ post = Post.first
74
+ post.subject = 'baz (de)'
75
+ post.save
76
+ assert_equal 'baz (de)', Post.first.subject
77
+ I18n.locale = :'en-US'
78
+ assert_equal 'foo (en)', Post.first.subject
79
+ end
80
+
81
+ test "saves an English post and loads test correctly" do
82
+ assert_nil Post.first
83
+ post = Post.create :subject => 'foo', :content => 'bar'
84
+ assert post.save
85
+ post = Post.first
86
+ assert_equal 'foo', post.subject
87
+ assert_equal 'bar', post.content
88
+ end
89
+
90
+ test "updates an attribute" do
91
+ post = Post.create :subject => 'foo', :content => 'bar'
92
+ post.update_attribute :subject, 'baz'
93
+ assert_equal 'baz', Post.first.subject
94
+ end
95
+
96
+ test "update_attributes failure" do
97
+ post = Post.create :subject => 'foo', :content => 'bar'
98
+ assert !post.update_attributes( { :subject => '' } )
99
+ assert_nil post.reload.attributes['subject']
100
+ assert_equal 'foo', post.subject
101
+ end
102
+
103
+ test "validates presence of :subject" do
104
+ post = Post.new
105
+ assert !post.save
106
+
107
+ post = Post.new :subject => 'foo'
108
+ assert post.save
109
+ end
110
+
111
+ test "returns the value for the correct locale, after locale switching" do
112
+ post = Post.create :subject => 'foo'
113
+ I18n.locale = 'de-DE'
114
+ post.subject = 'bar'
115
+ post.save
116
+ I18n.locale = 'en-US'
117
+ post = Post.first
118
+ assert_equal 'foo', post.subject
119
+ I18n.locale = 'de-DE'
120
+ assert_equal 'bar', post.subject
121
+ end
122
+
123
+ test "keeping one field in new locale when other field is changed" do
124
+ I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
125
+ post = Post.create :subject => 'foo'
126
+ I18n.locale = 'de-DE'
127
+ post.content = 'bar'
128
+ assert_equal 'foo', post.subject
129
+ end
130
+
131
+ test "modifying non-required field in a new locale" do
132
+ I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
133
+ post = Post.create :subject => 'foo'
134
+ I18n.locale = 'de-DE'
135
+ post.content = 'bar'
136
+ assert post.save
137
+ end
138
+
139
+ test "returns the value for the correct locale, after locale switching, without saving" do
140
+ post = Post.create :subject => 'foo'
141
+ I18n.locale = 'de-DE'
142
+ post.subject = 'bar'
143
+ I18n.locale = 'en-US'
144
+ assert_equal 'foo', post.subject
145
+ I18n.locale = 'de-DE'
146
+ assert_equal 'bar', post.subject
147
+ end
148
+
149
+ test "saves all locales, even after locale switching" do
150
+ post = Post.new :subject => 'foo'
151
+ I18n.locale = 'de-DE'
152
+ post.subject = 'bar'
153
+ I18n.locale = 'he-IL'
154
+ post.subject = 'baz'
155
+ post.save
156
+ I18n.locale = 'en-US'
157
+ post = Post.first
158
+ assert_equal 'foo', post.subject
159
+ I18n.locale = 'de-DE'
160
+ assert_equal 'bar', post.subject
161
+ I18n.locale = 'he-IL'
162
+ assert_equal 'baz', post.subject
163
+ end
164
+
165
+ test "resolves a simple fallback" do
166
+ I18n.locale = 'de-DE'
167
+ post = Post.create :subject => 'foo'
168
+ I18n.locale = 'de'
169
+ post.subject = 'baz'
170
+ post.content = 'bar'
171
+ post.save
172
+ I18n.locale = 'de-DE'
173
+ assert_equal 'foo', post.subject
174
+ assert_equal 'bar', post.content
175
+ end
176
+
177
+ test "resolves a simple fallback without reloading" do
178
+ I18n.locale = 'de-DE'
179
+ post = Post.new :subject => 'foo'
180
+ I18n.locale = 'de'
181
+ post.subject = 'baz'
182
+ post.content = 'bar'
183
+ I18n.locale = 'de-DE'
184
+ assert_equal 'foo', post.subject
185
+ assert_equal 'bar', post.content
186
+ end
187
+
188
+ test "resolves a complex fallback without reloading" do
189
+ I18n.fallbacks.map 'de' => %w(en he)
190
+ I18n.locale = 'de'
191
+ post = Post.new
192
+ I18n.locale = 'en'
193
+ post.subject = 'foo'
194
+ I18n.locale = 'he'
195
+ post.subject = 'baz'
196
+ post.content = 'bar'
197
+ I18n.locale = 'de'
198
+ assert_equal 'foo', post.subject
199
+ assert_equal 'bar', post.content
200
+ end
201
+
202
+ test "returns nil if no translations are found" do
203
+ post = Post.new :subject => 'foo'
204
+ assert_equal 'foo', post.subject
205
+ assert_nil post.content
206
+ end
207
+
208
+ test "returns nil if no translations are found; reloaded" do
209
+ post = Post.create :subject => 'foo'
210
+ post = Post.first
211
+ assert_equal 'foo', post.subject
212
+ assert_nil post.content
213
+ end
214
+
215
+ test "works with associations" do
216
+ blog = Blog.create
217
+ post1 = blog.posts.create :subject => 'foo'
218
+ I18n.locale = 'de-DE'
219
+ post2 = blog.posts.create :subject => 'bar'
220
+ assert_equal 2, blog.posts.size
221
+ I18n.locale = 'en-US'
222
+ assert_equal 'foo', blog.posts.first.subject
223
+ assert_nil blog.posts.last.subject
224
+ I18n.locale = 'de-DE'
225
+ assert_equal 'bar', blog.posts.last.subject
226
+ end
227
+
228
+ test "works with simple dynamic finders" do
229
+ foo = Post.create :subject => 'foo'
230
+ Post.create :subject => 'bar'
231
+ post = Post.find_by_subject('foo')
232
+ assert_equal foo, post
233
+ end
234
+
235
+ test 'change attribute on globalized model' do
236
+ post = Post.create :subject => 'foo', :content => 'bar'
237
+ assert_equal [], post.changed
238
+ post.subject = 'baz'
239
+ assert_equal [ 'subject' ], post.changed
240
+ post.content = 'quux'
241
+ assert_member 'subject', post.changed
242
+ assert_member 'content', post.changed
243
+ end
244
+
245
+ test 'change attribute on globalized model after locale switching' do
246
+ post = Post.create :subject => 'foo', :content => 'bar'
247
+ assert_equal [], post.changed
248
+ post.subject = 'baz'
249
+ I18n.locale = :de
250
+ assert_equal [ 'subject' ], post.changed
251
+ end
252
+
253
+ test 'fallbacks with lots of locale switching' do
254
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
255
+ post = Post.create :subject => 'foo'
256
+
257
+ I18n.locale = :'de-DE'
258
+ assert_equal 'foo', post.subject
259
+
260
+ I18n.locale = :'en-US'
261
+ post.update_attribute :subject, 'bar'
262
+
263
+ I18n.locale = :'de-DE'
264
+ assert_equal 'bar', post.subject
265
+ end
266
+
267
+ test 'reload' do
268
+ post = Post.create :subject => 'foo', :content => 'bar'
269
+ post.subject = 'baz'
270
+ assert_equal 'foo', post.reload.subject
271
+ end
272
+
273
+ test 'complex writing and stashing' do
274
+ post = Post.create :subject => 'foo', :content => 'bar'
275
+ post.subject = nil
276
+ assert_nil post.subject
277
+ assert !post.valid?
278
+ end
279
+
280
+ test 'translated class locale setting' do
281
+ assert ActiveRecord::Base.respond_to?(:locale)
282
+ assert_equal :'en-US', I18n.locale
283
+ assert_equal :'en-US', ActiveRecord::Base.locale
284
+ I18n.locale = :de
285
+ assert_equal :de, I18n.locale
286
+ assert_equal :de, ActiveRecord::Base.locale
287
+ ActiveRecord::Base.locale = :es
288
+ assert_equal :de, I18n.locale
289
+ assert_equal :es, ActiveRecord::Base.locale
290
+ I18n.locale = :fr
291
+ assert_equal :fr, I18n.locale
292
+ assert_equal :es, ActiveRecord::Base.locale
293
+ end
294
+
295
+ test "untranslated class responds to locale" do
296
+ assert Blog.respond_to?(:locale)
297
+ end
298
+
299
+ test "to ensure locales in different classes are the same" do
300
+ ActiveRecord::Base.locale = :de
301
+ assert_equal :de, ActiveRecord::Base.locale
302
+ assert_equal :de, Parent.locale
303
+ Parent.locale = :es
304
+ assert_equal :es, ActiveRecord::Base.locale
305
+ assert_equal :es, Parent.locale
306
+ end
307
+
308
+ test "attribute saving goes by content locale and not global locale" do
309
+ ActiveRecord::Base.locale = :de
310
+ assert_equal :'en-US', I18n.locale
311
+ Post.create :subject => 'foo'
312
+ assert_equal :de, Post.first.globalize_translations.first.locale
313
+ end
314
+
315
+ test "attribute loading goes by content locale and not global locale" do
316
+ post = Post.create :subject => 'foo'
317
+ assert_equal :'en-US', ActiveRecord::Base.locale
318
+ ActiveRecord::Base.locale = :de
319
+ assert_equal :'en-US', I18n.locale
320
+ post.update_attribute :subject, 'foo [de]'
321
+ assert_equal 'foo [de]', Post.first.subject
322
+ ActiveRecord::Base.locale = :'en-US'
323
+ assert_equal 'foo', Post.first.subject
324
+ end
325
+
326
+ test "access content locale before setting" do
327
+ Globalize::Model::ActiveRecord::Translated::ActMethods.class_eval "remove_class_variable(:@@locale)"
328
+ assert_nothing_raised { ActiveRecord::Base.locale }
329
+ end
330
+
331
+ test "translated_locales" do
332
+ Post.locale = :de
333
+ post = Post.create :subject => 'foo'
334
+ Post.locale = :es
335
+ post.update_attribute :subject, 'bar'
336
+ Post.locale = :fr
337
+ post.update_attribute :subject, 'baz'
338
+ assert_equal [ :de, :es, :fr ], post.translated_locales
339
+ assert_equal [ :de, :es, :fr ], Post.first.translated_locales
340
+ end
341
+
342
+ test "saving record correctly after post-save reload" do
343
+ reloader = Reloader.create :content => 'foo'
344
+ assert_equal 'foo', reloader.content
345
+ end
346
+
347
+ test "including globalize_translations" do
348
+ I18n.locale = :de
349
+ Post.create :subject => "Foo1", :content => "Bar1"
350
+ Post.create :subject => "Foo2", :content => "Bar2"
351
+
352
+ class << Post
353
+ def tranlsations_included
354
+ self.all(:include => :globalize_translations)
355
+ end
356
+ end
357
+
358
+ default = Post.all.map {|x| [x.subject, x.content]}
359
+ with_include = Post.tranlsations_included.map {|x| [x.subject, x.content]}
360
+ assert_equal default, with_include
361
+ end
362
+
363
+ test "setting multiple translations at once with options hash" do
364
+ Post.locale = :de
365
+ post = Post.create :subject => "foo1", :content => "foo1"
366
+ Post.locale = :en
367
+ post.update_attributes( :subject => "bar1", :content => "bar1" )
368
+
369
+ options = { :de => {:subject => "foo2", :content => "foo2"},
370
+ :en => {:subject => "bar2", :content => "bar2"} }
371
+ post.set_translations options
372
+ post.reload
373
+
374
+ assert ["bar2", "bar2"], [post.subject, post.content]
375
+ Post.locale = :de
376
+ assert ["foo2", "foo2"], [post.subject, post.content]
377
+ end
378
+
379
+ test "setting only one translation with set_translations" do
380
+ Post.locale = :de
381
+ post = Post.create :subject => "foo1", :content => "foo1"
382
+ Post.locale = :en
383
+ post.update_attributes( :subject => "bar1", :content => "bar1" )
384
+
385
+ options = { :en => {:subject => "bar2", :content => "bar2"} }
386
+ post.set_translations options
387
+ post.reload
388
+
389
+ assert ["bar2", "bar2"], [post.subject, post.content]
390
+ Post.locale = :de
391
+ assert ["foo1", "foo1"], [post.subject, post.content]
392
+ end
393
+
394
+ test "setting only selected attributes with set_translations" do
395
+ Post.locale = :de
396
+ post = Post.create :subject => "foo1", :content => "foo1"
397
+ Post.locale = :en
398
+ post.update_attributes( :subject => "bar1", :content => "bar1" )
399
+
400
+ options = { :de => {:content => "foo2"}, :en => {:subject => "bar2"} }
401
+ post.set_translations options
402
+ post.reload
403
+
404
+ assert ["bar2", "bar1"], [post.subject, post.content]
405
+ Post.locale = :de
406
+ assert ["foo1", "foo2"], [post.subject, post.content]
407
+ end
408
+
409
+ test "setting invalid attributes raises ArgumentError" do
410
+ Post.locale = :de
411
+ post = Post.create :subject => "foo1", :content => "foo1"
412
+ Post.locale = :en
413
+ post.update_attributes( :subject => "bar1", :content => "bar1" )
414
+
415
+ options = { :de => {:fake => "foo2"} }
416
+ exception = assert_raise(ActiveRecord::UnknownAttributeError) do
417
+ post.set_translations options
418
+ end
419
+ assert_equal "unknown attribute: fake", exception.message
420
+ end
421
+
422
+ test "reload accepting find options" do
423
+ p = Post.create :subject => "Foo", :content => "Bar"
424
+ assert p.reload(:readonly => true, :lock => true)
425
+ assert_raise(ArgumentError) { p.reload(:foo => :bar) }
426
+ end
427
+
428
+ test "dependent destroy of translation" do
429
+ p = Post.create :subject => "Foo", :content => "Bar"
430
+ assert_equal 1, PostTranslation.count
431
+ p.destroy
432
+ assert_equal 0, PostTranslation.count
433
+ end
434
+
435
+ test "translating subclass of untranslated comment model" do
436
+ translated_comment = TranslatedComment.create(:post => @post)
437
+ assert_nothing_raised { translated_comment.globalize_translations }
438
+ end
439
+
440
+ test "modifiying translated comments works as expected" do
441
+ I18n.locale = :en
442
+ translated_comment = TranslatedComment.create(:post => @post, :content => 'foo')
443
+ assert_equal 'foo', translated_comment.content
444
+
445
+ I18n.locale = :de
446
+ translated_comment.content = 'bar'
447
+ assert translated_comment.save
448
+ assert_equal 'bar', translated_comment.content
449
+
450
+ I18n.locale = :en
451
+ assert_equal 'foo', translated_comment.content
452
+
453
+ assert_equal 2, translated_comment.globalize_translations.size
454
+ end
455
+
456
+ test "can create a proxy class for a namespaced model" do
457
+ module Foo
458
+ module Bar
459
+ class Baz < ActiveRecord::Base
460
+ translates :bumm
461
+ end
462
+ end
463
+ end
464
+ end
465
+
466
+ test "attribute translated before type cast" do
467
+ Post.locale = :en
468
+ post = Post.create :subject => 'foo', :content => 'bar'
469
+ Post.locale = :de
470
+ post.update_attribute :subject, "German foo"
471
+ assert_equal 'German foo', post.subject_before_type_cast
472
+ Post.locale = :en
473
+ assert_equal 'foo', post.subject_before_type_cast
474
+ end
475
+
476
+ test "don't override existing translation model" do
477
+ assert PostTranslation.new.respond_to?(:existing_method)
478
+ end
479
+ end
480
+
481
+ # TODO should validate_presence_of take fallbacks into account? maybe we need
482
+ # an extra validation call, or more options for validate_presence_of.
483
+ # TODO error checking for fields that exist in main table, don't exist in
484
+ # proxy table, aren't strings or text
485
+ #
486
+ # TODO allow finding by translated attributes in conditions?
487
+ # TODO generate advanced dynamic finders?