galetahub-globalize3 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README.textile +206 -0
  2. data/Rakefile +22 -0
  3. data/lib/globalize.rb +59 -0
  4. data/lib/globalize/active_record.rb +13 -0
  5. data/lib/globalize/active_record/accessors.rb +22 -0
  6. data/lib/globalize/active_record/act_macro.rb +67 -0
  7. data/lib/globalize/active_record/adapter.rb +101 -0
  8. data/lib/globalize/active_record/attributes.rb +27 -0
  9. data/lib/globalize/active_record/class_methods.rb +125 -0
  10. data/lib/globalize/active_record/exceptions.rb +19 -0
  11. data/lib/globalize/active_record/instance_methods.rb +166 -0
  12. data/lib/globalize/active_record/migration.rb +125 -0
  13. data/lib/globalize/active_record/translation.rb +37 -0
  14. data/lib/globalize/engine.rb +17 -0
  15. data/lib/globalize/utils.rb +142 -0
  16. data/lib/globalize/versioning.rb +5 -0
  17. data/lib/globalize/versioning/paper_trail.rb +41 -0
  18. data/lib/globalize3.rb +1 -0
  19. data/lib/globalize3/version.rb +3 -0
  20. data/lib/i18n/missing_translations_log_handler.rb +41 -0
  21. data/lib/i18n/missing_translations_raise_handler.rb +25 -0
  22. data/lib/patches/active_record/query_method.rb +35 -0
  23. data/lib/patches/active_record/xml_attribute_serializer.rb +13 -0
  24. data/lib/tasks/globalize.rake +13 -0
  25. data/test/all.rb +1 -0
  26. data/test/data/models.rb +68 -0
  27. data/test/data/schema.rb +108 -0
  28. data/test/globalize3/attributes_test.rb +133 -0
  29. data/test/globalize3/clone_test.rb +58 -0
  30. data/test/globalize3/dirty_tracking_test.rb +61 -0
  31. data/test/globalize3/dynamic_finders_test.rb +171 -0
  32. data/test/globalize3/fallbacks_test.rb +146 -0
  33. data/test/globalize3/locale_test.rb +81 -0
  34. data/test/globalize3/migration_test.rb +156 -0
  35. data/test/globalize3/set_translations_test.rb +54 -0
  36. data/test/globalize3/translation_class_test.rb +59 -0
  37. data/test/globalize3/validations_test.rb +92 -0
  38. data/test/globalize3/versioning_test.rb +87 -0
  39. data/test/globalize3_test.rb +159 -0
  40. data/test/i18n/missing_translations_test.rb +35 -0
  41. data/test/test_helper.rb +105 -0
  42. metadata +243 -0
@@ -0,0 +1,146 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class TranslatedTest < Test::Unit::TestCase
4
+ def setup
5
+ @previous_backend = I18n.backend
6
+ I18n.pretend_fallbacks
7
+ I18n.backend = BackendWithFallbacks.new
8
+
9
+ I18n.locale = :'en-US'
10
+ I18n.fallbacks = ::I18n::Locale::Fallbacks.new
11
+ end
12
+
13
+ def teardown
14
+ I18n.fallbacks.clear
15
+ I18n.hide_fallbacks
16
+ I18n.backend = @previous_backend
17
+ end
18
+
19
+ test "keeping one field in new locale when other field is changed" do
20
+ I18n.fallbacks.map('de-DE' => [ 'en-US' ])
21
+ post = Post.create :title => 'foo'
22
+
23
+ I18n.locale = 'de-DE'
24
+ post.content = 'bar'
25
+
26
+ assert_equal 'foo', post.title
27
+ end
28
+
29
+ test "modifying non-required field in a new locale" do
30
+ I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
31
+ post = Post.create :title => 'foo'
32
+ I18n.locale = 'de-DE'
33
+ post.content = 'bar'
34
+ assert post.save
35
+ end
36
+
37
+ test "resolves a simple fallback" do
38
+ I18n.locale = 'de-DE'
39
+ post = Post.create :title => 'foo'
40
+
41
+ I18n.locale = 'de'
42
+ post.title = 'baz'
43
+ post.content = 'bar'
44
+ post.save!
45
+
46
+ I18n.locale = 'de-DE'
47
+ assert_equal 'foo', post.title
48
+ assert_equal 'bar', post.content
49
+ end
50
+
51
+ test "resolves a simple fallback without reloading" do
52
+ I18n.locale = 'de-DE'
53
+ post = Post.new :title => 'foo'
54
+
55
+ I18n.locale = 'de'
56
+ post.title = 'baz'
57
+ post.content = 'bar'
58
+
59
+ I18n.locale = 'de-DE'
60
+ assert_equal 'foo', post.title
61
+ assert_equal 'bar', post.content
62
+ end
63
+
64
+ test "resolves a complex fallback without reloading" do
65
+ I18n.fallbacks.map 'de' => %w(en he)
66
+ I18n.locale = 'de'
67
+ post = Post.new
68
+ I18n.locale = 'en'
69
+ post.title = 'foo'
70
+ I18n.locale = 'he'
71
+ post.title = 'baz'
72
+ post.content = 'bar'
73
+ I18n.locale = 'de'
74
+ assert_equal 'foo', post.title
75
+ assert_equal 'bar', post.content
76
+ end
77
+
78
+ test 'fallbacks with lots of locale switching' do
79
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
80
+ post = Post.create :title => 'foo'
81
+
82
+ I18n.locale = :'de-DE'
83
+ assert_equal 'foo', post.title
84
+
85
+ I18n.locale = :'en-US'
86
+ post.update_attributes(:title => 'bar')
87
+
88
+ I18n.locale = :'de-DE'
89
+ assert_equal 'bar', post.title
90
+ end
91
+
92
+ test 'fallbacks with lots of locale switching 2' do
93
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
94
+ child = Child.create :content => 'foo'
95
+
96
+ I18n.locale = :'de-DE'
97
+ assert_equal 'foo', child.content
98
+
99
+ I18n.locale = :'en-US'
100
+ child.update_attributes(:content => 'bar')
101
+
102
+ I18n.locale = :'de-DE'
103
+ assert_equal 'bar', child.content
104
+ end
105
+
106
+ test 'fallbacks with nil translations' do
107
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
108
+ post = Post.create :title => 'foo'
109
+
110
+ I18n.locale = :'de-DE'
111
+ assert_equal 'foo', post.title
112
+
113
+ post.update_attribute :title, nil
114
+ assert_equal 'foo', post.title
115
+ end
116
+
117
+ test 'fallbacks with empty translations' do
118
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
119
+ task = Task.create :name => 'foo'
120
+
121
+ I18n.locale = :'de-DE'
122
+ assert_equal 'foo', task.name
123
+
124
+ task.update_attribute :name, ''
125
+ assert_equal 'foo', task.name
126
+ end
127
+
128
+ test 'fallbacks with empty translations 2' do
129
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
130
+ task = Task.create :name => 'foo'
131
+ post = Post.create :title => 'foo'
132
+
133
+ I18n.locale = :'de-DE'
134
+ assert_equal 'foo', task.name
135
+ assert_equal 'foo', post.title
136
+
137
+ task.update_attribute :name, ''
138
+ assert_equal 'foo', task.name
139
+
140
+ post.update_attribute :title, ''
141
+ assert_equal '', post.title
142
+ end
143
+ end
144
+ # TODO should validate_presence_of take fallbacks into account? maybe we need
145
+ # an extra validation call, or more options for validate_presence_of.
146
+
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ class LocaleTest < Test::Unit::TestCase
6
+ test "Globalize has locale accessors" do
7
+ assert Globalize.respond_to?(:locale)
8
+ assert Globalize.respond_to?(:locale=)
9
+ end
10
+
11
+ test "Globalize.locale reader can be called before a locale was set" do
12
+ Globalize.locale = nil
13
+ assert_nothing_raised { Globalize.locale }
14
+ end
15
+
16
+ test 'Globalize locale setting' do
17
+ assert_equal :en, I18n.locale
18
+ assert_equal :en, Globalize.locale
19
+
20
+ I18n.locale = :de
21
+ assert_equal :de, I18n.locale
22
+ assert_equal :de, Globalize.locale
23
+
24
+ Globalize.locale = :es
25
+ assert_equal :de, I18n.locale
26
+ assert_equal :es, Globalize.locale
27
+
28
+ I18n.locale = :fr
29
+ assert_equal :fr, I18n.locale
30
+ assert_equal :es, Globalize.locale
31
+ end
32
+
33
+ test "Globalize locale setting with strings" do
34
+ I18n.locale = 'de'
35
+ Globalize.locale = 'de'
36
+ assert_equal I18n.locale, Globalize.locale
37
+
38
+ I18n.locale = 'de'
39
+ Globalize.locale = :de
40
+ assert_equal I18n.locale, Globalize.locale
41
+
42
+ I18n.locale = :de
43
+ Globalize.locale = 'de'
44
+ assert_equal I18n.locale, Globalize.locale
45
+ end
46
+
47
+ test 'with_locale temporarily sets the given locale and yields the block' do
48
+ assert_equal :en, Globalize.locale
49
+ Globalize.with_locale :de do |locale|
50
+ assert_equal :de, Globalize.locale
51
+ assert_equal :de, locale
52
+ end
53
+ assert_equal :en, Globalize.locale
54
+ end
55
+
56
+ test 'with_locales calls block once with each locale given temporarily set' do
57
+ locales = Globalize.with_locales :en, [:de, :fr] do |locale|
58
+ assert_equal locale, Globalize.locale
59
+ locale
60
+ end
61
+ assert_equal [:en, :de, :fr], locales
62
+ end
63
+
64
+ test "attribute saving goes by content locale and not global locale" do
65
+ Globalize.locale = :de
66
+ assert_equal :en, I18n.locale
67
+ Post.create :title => 'foo'
68
+ assert_equal :de, Post.first.translations.first.locale
69
+ end
70
+
71
+ test "attribute loading goes by content locale and not global locale" do
72
+ post = Post.create(:title => 'title')
73
+ assert_translated Post.first, :en, :title, 'title'
74
+
75
+ Globalize.locale = :de
76
+ post.update_attributes(:title => 'Titel')
77
+
78
+ assert_translated Post.first, :en, :title, 'title'
79
+ assert_translated Post.first, :de, :title, 'Titel'
80
+ end
81
+ end
@@ -0,0 +1,156 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class MigrationTest < Test::Unit::TestCase
4
+ include Globalize::ActiveRecord::Exceptions
5
+
6
+ def setup
7
+ super
8
+ reset_schema(Migrated, MigratedWithUltraLongModelName)
9
+ assert !Migrated::Translation.table_exists?
10
+ assert !Migrated::Translation.index_exists_on?(:migrated_id)
11
+ end
12
+
13
+ def teardown
14
+ reset_schema(Migrated, MigratedWithUltraLongModelName)
15
+ end
16
+
17
+ test 'create_translation_table!(:name => :text) adds the translations table' do
18
+ Migrated.create_translation_table!(:name => :text)
19
+ assert_migration_table(:name => :text)
20
+ end
21
+
22
+ test 'create_translation_table! adds the translations table using the column type from the translated model' do
23
+ Migrated.create_translation_table!
24
+ assert_migration_table(:name => :string)
25
+ end
26
+
27
+ test 'create_translation_table! can not be called on non-translated models' do
28
+ assert_raise NoMethodError do
29
+ Blog.create_translation_table!(:name => :string)
30
+ end
31
+ end
32
+
33
+ test 'passing a non-translated field name raises BadFieldName' do
34
+ assert_raise BadFieldName do
35
+ Migrated.create_translation_table!(:content => :text)
36
+ end
37
+ end
38
+
39
+ test 'passing a translated field with a wrong type raises BadFieldType' do
40
+ assert_raise BadFieldType do
41
+ Migrated.create_translation_table!(:name => :integer)
42
+ end
43
+ end
44
+
45
+ test 'drop_translation_table! drops the translations table' do
46
+ Migrated.create_translation_table!(:name => :string)
47
+ assert Migrated::Translation.table_exists?
48
+ assert Migrated::Translation.index_exists_on?(:migrated_id)
49
+
50
+ Migrated.drop_translation_table!
51
+ assert !Migrated::Translation.table_exists?
52
+ assert !Migrated::Translation.index_exists_on?(:migrated_id)
53
+ end
54
+
55
+ test 'drop_translation_table! can not be called on non-translated models' do
56
+ assert_raise NoMethodError do
57
+ Blog.drop_translation_table!
58
+ end
59
+ end
60
+
61
+ test "translation_index_name returns a readable index name if it's not longer than 50 characters" do
62
+ assert_equal 'index_migrated_translations_on_migrated_id', Migrated.send(:translation_index_name)
63
+ end
64
+
65
+ test "translation_index_name returns a hashed index name if it's longer than 50 characters" do
66
+ assert_match /^index_[a-z0-9]{40}$/, MigratedWithUltraLongModelName.send(:translation_index_name)
67
+ end
68
+
69
+ test 'create_translation_table! can deal with ultra long table names' do
70
+ model = MigratedWithUltraLongModelName
71
+ model.create_translation_table!(:name => :string)
72
+
73
+ assert model::Translation.table_exists?
74
+ assert model::Translation.index_exists?(model.send(:translation_index_name))
75
+ end
76
+
77
+ test 'drop_translation_table! can deal with ultra long table names' do
78
+ model = MigratedWithUltraLongModelName
79
+ model.create_translation_table!(:name => :string)
80
+ model.drop_translation_table!
81
+
82
+ assert !model::Translation.table_exists?
83
+ assert !model::Translation.index_exists?(:ultra_long_model_name_without_proper_id)
84
+ end
85
+
86
+ # This test is relatively exhaustive in that it tests the full stack of
87
+ # create_translation_table! and its ability to use migrate_data to migrate
88
+ # non translated data into the default Globalize locale.
89
+ # We are then testing the ability of drop_translation_table! to migrate the
90
+ # translated data from the default Globalize locale back as untranslated data.
91
+ test 'create_translation_table! with option migrate_data set to true DOES migrate existing data AND rolls back' do
92
+ # Ensure we have a "Fresh" version. Can't use reset_schema because it's not a translated model, yet.
93
+ model = Untranslated
94
+ model.drop_translation_table! if model.respond_to?(:drop_translation_table!)
95
+ model.reset_column_information rescue nil
96
+
97
+ # First create an untranslated record
98
+ untranslated = model.create! :name => 'Untranslated'
99
+
100
+ # Now add translation support and migrate (also tests .untranslated_attributes)
101
+ model.instance_eval %{ translates :name }
102
+ model.create_translation_table!({:name => :string}, {:migrate_data => true})
103
+ assert model::Translation.table_exists?
104
+
105
+ # Reload the untranslated record
106
+ untranslated.reload
107
+
108
+ # Was it migrated?
109
+ assert_translated untranslated, :en, :name, 'Untranslated'
110
+
111
+ # Cool, now we need to get rid of the non-translated value for the next test
112
+ model.update_all({:name => 'No longer translated'}, :id => untranslated.id)
113
+ untranslated.reload
114
+
115
+ # Make sure we didn't harm the translation and that it's been set. (also tests .untranslated_attributes)
116
+ assert_equal 'No longer translated', untranslated.untranslated_attributes['name']
117
+ assert_translated untranslated, :en, :name, 'Untranslated'
118
+
119
+ # Now we need to rollback then undo
120
+ model.drop_translation_table! :migrate_data => true
121
+ model.reset_column_information rescue nil
122
+ assert !model::Translation.table_exists?
123
+ untranslated.reload
124
+
125
+ # Was it restored? (also tests .untranslated_attributes)
126
+ assert_equal 'Untranslated', untranslated.untranslated_attributes['name']
127
+ end
128
+
129
+ protected
130
+
131
+ def reset_schema(*models)
132
+ models.each do |model|
133
+ model.reset_column_information rescue nil
134
+ model::Translation.reset_column_information rescue nil
135
+ model.drop_translation_table! if model::Translation.table_exists?
136
+ end
137
+ end
138
+
139
+ def column_type(name)
140
+ Migrated::Translation.columns.detect { |c| c.name == name.to_s }.try(:type)
141
+ end
142
+
143
+ def assert_migration_table(fields)
144
+ assert Migrated::Translation.table_exists?
145
+ assert Migrated::Translation.index_exists_on?(:migrated_id)
146
+
147
+ assert_equal :string, column_type(:locale)
148
+ assert_equal :integer, column_type(:migrated_id)
149
+ assert_equal :datetime, column_type(:created_at)
150
+ assert_equal :datetime, column_type(:updated_at)
151
+
152
+ fields.each do |name, type|
153
+ assert_equal type, column_type(name)
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ class AttributesTest < Test::Unit::TestCase
6
+ test "set_translations sets multiple translations at once" do
7
+ post = Post.create(:title => 'title', :content => 'content', :locale => :en)
8
+ post.update_attributes(:title => 'Titel', :content => 'Inhalt', :locale => :de)
9
+
10
+ post.set_translations(
11
+ :en => { :title => 'updated title', :content => 'updated content' },
12
+ :de => { :title => 'geänderter Titel', :content => 'geänderter Inhalt' }
13
+ )
14
+ post.reload
15
+
16
+ assert_translated post, :en, [:title, :content], ['updated title', 'updated content']
17
+ assert_translated post, :de, [:title, :content], ['geänderter Titel', 'geänderter Inhalt']
18
+ end
19
+
20
+ test "set_translations does not touch existing translations for other locales" do
21
+ post = Post.create(:title => 'title', :content => 'content', :locale => :en)
22
+ post.update_attributes(:title => 'Titel', :content => 'Inhalt', :locale => :de)
23
+
24
+ post.set_translations(:en => { :title => 'updated title', :content => 'updated content' })
25
+ post.reload
26
+
27
+ assert_translated post, :en, [:title, :content], ['updated title', 'updated content']
28
+ assert_translated post, :de, [:title, :content], ['Titel', 'Inhalt']
29
+ end
30
+
31
+ test "set_translations does not touch existing translations for other attributes" do
32
+ post = Post.create(:title => 'title', :content => 'content', :locale => :en)
33
+ post.update_attributes(:title => 'Titel', :content => 'Inhalt', :locale => :de)
34
+
35
+ post.set_translations(
36
+ :en => { :title => "updated title" },
37
+ :de => { :content => "geänderter Inhalt" }
38
+ )
39
+ post.reload
40
+
41
+ assert_translated post, :en, [:title, :content], ['updated title', 'content']
42
+ assert_translated post, :de, [:title, :content], ['Titel', 'geänderter Inhalt']
43
+ end
44
+
45
+ test "set_translations raises an UnknownAttributeError on unknown attributes" do
46
+ post = Post.create(:title => 'title', :content => 'content', :locale => :en)
47
+ post.update_attributes(:title => 'Titel', :content => 'Inhalt', :locale => :de)
48
+
49
+ options = { :de => { :does_not_exist => 'should raise' } }
50
+ assert_raises(ActiveRecord::UnknownAttributeError, 'unknown attribute: does_not_exist') do
51
+ post.set_translations options
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class TranslationClassTest < Test::Unit::TestCase
4
+ test 'translation_class returns the Translation class' do
5
+ assert_equal Post::Translation, Post.translation_class
6
+ end
7
+
8
+ test 'defines a Translation class nested in the model class' do
9
+ assert Post.const_defined?(:Translation)
10
+ end
11
+
12
+ test 'defines a belongs_to association' do
13
+ assert_belongs_to Post::Translation, :post
14
+ end
15
+
16
+ test 'defines a reader for :locale that returns a symbol' do
17
+ post = Post::Translation.new
18
+ post.send(:write_attribute, 'locale', 'de')
19
+ assert_equal :de, post.locale
20
+ end
21
+
22
+ test 'defines a write for :locale that writes a string' do
23
+ post = Post::Translation.new
24
+ post.locale = :de
25
+ assert_equal 'de', post.read_attribute('locale')
26
+ end
27
+
28
+ test "can create a translation class for a namespaced model" do
29
+ assert_nothing_raised do
30
+ module Foo
31
+ module Bar
32
+ class Baz < ActiveRecord::Base
33
+ translates :bumm
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ test "can create a translation class for a model with an uppercase table name" do
41
+ assert_nothing_raised do
42
+ UppercaseTableName.create
43
+ end
44
+ end
45
+
46
+ test "does not override existing translation class" do
47
+ assert PostTranslation.new.respond_to?(:existing_method)
48
+ end
49
+
50
+ test "required_attributes returns required attributes (i.e. validates_presence_of)" do
51
+ assert_equal [:name, :email], User.required_attributes
52
+ end
53
+
54
+ test "required_translated_attributes do not include non-translated attributes" do
55
+ assert_equal [:name], User.required_translated_attributes
56
+ end
57
+ end
58
+
59
+