galetahub-globalize3 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+