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,92 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ def teardown
5
+ super
6
+ Validatee.reset_callbacks(:validate)
7
+ end
8
+
9
+ # TODO
10
+ #
11
+ # test "a record with valid values on non-default locale validates" do
12
+ # assert Post.create(:title => 'foo', :locale => :de).valid?
13
+ # end
14
+
15
+ test "update_attribute succeeds with valid values" do
16
+ post = Post.create(:title => 'foo')
17
+ post.update_attributes(:title => 'baz')
18
+ assert post.valid?
19
+ assert_equal 'baz', Post.first.title
20
+ end
21
+
22
+ test "update_attributes fails with invalid values" do
23
+ post = Post.create(:title => 'foo')
24
+ assert !post.update_attributes(:title => '')
25
+ assert !post.valid?
26
+ assert_not_nil post.reload.attributes['title']
27
+ assert_equal 'foo', post.title
28
+ end
29
+
30
+ test "validates_presence_of" do
31
+ Validatee.class_eval { validates_presence_of :string }
32
+ assert !Validatee.new.valid?
33
+ assert Validatee.new(:string => 'foo').valid?
34
+ end
35
+
36
+ test "validates_confirmation_of" do
37
+ Validatee.class_eval { validates_confirmation_of :string }
38
+ assert !Validatee.new(:string => 'foo', :string_confirmation => 'bar').valid?
39
+ assert Validatee.new(:string => 'foo', :string_confirmation => 'foo').valid?
40
+ end
41
+
42
+ test "validates_acceptance_of" do
43
+ Validatee.class_eval { validates_acceptance_of :string, :accept => '1' }
44
+ assert !Validatee.new(:string => '0').valid?
45
+ assert Validatee.new(:string => '1').valid?
46
+ end
47
+
48
+ test "validates_length_of (:is)" do
49
+ Validatee.class_eval { validates_length_of :string, :is => 1 }
50
+ assert !Validatee.new(:string => 'aa').valid?
51
+ assert Validatee.new(:string => 'a').valid?
52
+ end
53
+
54
+ test "validates_format_of" do
55
+ Validatee.class_eval { validates_format_of :string, :with => /^\d+$/ }
56
+ assert !Validatee.new(:string => 'a').valid?
57
+ assert Validatee.new(:string => '1').valid?
58
+ end
59
+
60
+ test "validates_inclusion_of" do
61
+ Validatee.class_eval { validates_inclusion_of :string, :in => %(a) }
62
+ assert !Validatee.new(:string => 'b').valid?
63
+ assert Validatee.new(:string => 'a').valid?
64
+ end
65
+
66
+ test "validates_exclusion_of" do
67
+ Validatee.class_eval { validates_exclusion_of :string, :in => %(b) }
68
+ assert !Validatee.new(:string => 'b').valid?
69
+ assert Validatee.new(:string => 'a').valid?
70
+ end
71
+
72
+ test "validates_numericality_of" do
73
+ Validatee.class_eval { validates_numericality_of :string }
74
+ assert !Validatee.new(:string => 'a').valid?
75
+ assert Validatee.new(:string => '1').valid?
76
+ end
77
+
78
+ # This doesn't pass and Rails' validates_uniqueness_of implementation doesn't
79
+ # seem to be extensible easily. One can work around that by either defining
80
+ # a custom validation on the Validatee model itself, or by using validates_uniqueness_of
81
+ # on Validatee::Translation.
82
+ #
83
+ # test "validates_uniqueness_of" do
84
+ # Validatee.class_eval { validates_uniqueness_of :string }
85
+ # Validatee.create!(:string => 'a')
86
+ # assert !Validatee.new(:string => 'a').valid?
87
+ # assert Validatee.new(:string => 'b').valid?
88
+ # end
89
+
90
+ # test "validates_associated" do
91
+ # end
92
+ end
@@ -0,0 +1,87 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class VersioningTest < Test::Unit::TestCase
4
+ test "versions are scoped to the current Globalize locale" do
5
+ post = Post.create!(:title => 'title v1', :content => '')
6
+ post.update_attributes!(:title => 'title v2')
7
+ # Creates a 'created' version, an initial update, and the update
8
+ assert_equal ['en', 'en', 'en'], post.versions.map(&:locale)
9
+
10
+ Globalize.locale = :de
11
+ post.update_attributes!(:title => 'Titel v1')
12
+ assert_equal ['de', 'de'], post.versions.map(&:locale)
13
+
14
+ Globalize.locale = :en
15
+ post.versions.reset # hrmmm.
16
+ assert_equal ['en', 'en', 'en'], post.versions.map(&:locale)
17
+ end
18
+
19
+ test "does not create a version for initial locale" do
20
+ # really ?
21
+ end
22
+
23
+ test "reverting to an earlier version only reverts changes to the current locale" do
24
+ post = Post.create!(:title => 'title v1', :content => '')
25
+ post.update_attributes!(:title => 'title v2')
26
+ post.update_attributes!(:title => 'Titel v1', :locale => :de)
27
+ post.update_attributes!(:title => 'title v3')
28
+
29
+ # Roll back 2 versions in default locale
30
+ post.rollback
31
+ post.rollback
32
+
33
+ assert_equal 'title v1', post.title(:en)
34
+ assert_equal 'Titel v1', post.title(:de)
35
+ end
36
+
37
+ test "reverting happens per locale" do
38
+ post = Post.create!(:title => 'title v1')
39
+
40
+ with_locale(:en) do
41
+ post.update_attributes!(:title => 'title v2')
42
+ end
43
+
44
+ with_locale(:de) do
45
+ post.update_attributes!(:title => 'Titel v1')
46
+ end
47
+
48
+ with_locale(:en) do
49
+ post.update_attributes!(:title => 'title v3', :published => true)
50
+ assert post.published?
51
+ end
52
+
53
+ with_locale(:de) do
54
+ post.update_attributes!(:title => 'Titel v2')
55
+ assert !post.published?
56
+ end
57
+
58
+ with_locale(:en) do
59
+ post.rollback
60
+ assert_equal 'title v2', post.title
61
+ assert !post.published?
62
+
63
+ post.rollback
64
+ assert_equal 'title v1', post.title
65
+ assert !post.published?
66
+ end
67
+
68
+ with_locale(:de) do
69
+ post.rollback
70
+ assert_equal 'Titel v1', post.title
71
+ assert !post.published?
72
+ end
73
+
74
+ with_locale(:en) do
75
+ assert_equal 'title v1', post.title
76
+ assert !post.published?
77
+ end
78
+ end
79
+
80
+ # test "versioning with vestal_versions works (using update_attribute)" do
81
+ # post = Post.create!(:title => 'title', :content => '')
82
+ # assert_equal 1, post.version
83
+ #
84
+ # assert post.update_attribute(:title, 'changed title')
85
+ # assert_equal 2, post.version
86
+ # end
87
+ end
@@ -0,0 +1,159 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ class Globalize3Test < Test::Unit::TestCase
6
+ test "a translated record has many translations" do
7
+ assert_has_many(Post, :translations)
8
+ end
9
+
10
+ test "translations are empty for a new record" do
11
+ assert_equal [], Post.new.translations
12
+ end
13
+
14
+ test "create uses the given locale" do
15
+ post = Post.create(:title => 'Titel', :locale => :de)
16
+ assert_translated post, :de, :title, 'Titel'
17
+ end
18
+
19
+ test "can translate boolean values" do
20
+ post = Post.create(:title => 'Titel', :published => true, :locale => :de)
21
+ assert_translated post, :de, :published, true
22
+ end
23
+
24
+ test "can translate datetime values" do
25
+ now = Time.now
26
+ post = Post.create(:title => 'Titel', :published_at => now, :locale => :de)
27
+ assert_translated post, :de, :published_at, now
28
+ end
29
+
30
+ test "attributes= uses the given locale" do
31
+ post = Post.create(:title => 'title')
32
+ post.attributes = { :title => 'Titel', :locale => :de }
33
+ post.save
34
+ post.reload
35
+
36
+ assert_equal 2, post.translations.size
37
+ assert_translated post, :de, :title, 'Titel'
38
+ assert_translated post, :en, :title, 'title'
39
+ end
40
+
41
+ test "create on associations works" do
42
+ blog = Blog.create
43
+ blog.posts.create(:title => 'title')
44
+ blog.posts.create(:title => 'Titel', :locale => :de)
45
+
46
+ assert_equal 2, blog.posts.size
47
+ assert_translated blog.posts.first, :en, :title, 'title'
48
+ assert_translated blog.posts.last, :de, :title, 'Titel'
49
+ end
50
+
51
+ test "named scopes work" do
52
+ post = Blog.create.posts.create(:title => 'some title')
53
+ post.reload
54
+
55
+ assert_translated post, :en, :title, 'some title'
56
+ end
57
+
58
+ test "saves a translations record for each locale using a given locale" do
59
+ post = Post.create(:title => 'Titel', :locale => :de)
60
+ post.update_attributes(:title => 'title', :locale => :en)
61
+
62
+ assert_equal 2, post.translations.size
63
+ assert_translated post, :de, :title, 'Titel'
64
+ assert_translated post, :en, :title, 'title'
65
+ end
66
+
67
+ test "saves a translations record for each locale using the current I18n locale" do
68
+ post = with_locale(:de) { Post.create(:title => 'Titel') }
69
+ with_locale(:en) { post.update_attributes(:title => 'title') }
70
+
71
+ assert_equal 2, post.translations.size
72
+ assert_translated post, :en, :title, 'title'
73
+ assert_translated post, :de, :title, 'Titel'
74
+ end
75
+
76
+ test "reload works with translated attributes" do
77
+ post = Post.create(:title => 'foo')
78
+ post.title = 'baz'
79
+ post.reload
80
+ assert_equal 'foo', post.title
81
+ end
82
+
83
+ test "reload accepts standard finder options" do
84
+ post = Post.create(:title => "title")
85
+ assert post.reload(:readonly => true, :lock => true)
86
+ assert_raise(ArgumentError) { post.reload(:foo => :bar) }
87
+ end
88
+
89
+ test "destroy destroys dependent translations" do
90
+ post = Post.create(:title => "title")
91
+ post.update_attributes(:title => 'Titel', :locale => :de)
92
+ assert_equal 2, PostTranslation.count
93
+ post.destroy
94
+ assert_equal 0, PostTranslation.count
95
+ end
96
+
97
+ test "to_xml includes translated fields" do
98
+ post = Post.create(:title => "foo", :content => "bar")
99
+ post.reload
100
+ assert post.to_xml =~ %r(<title>foo</title>)
101
+ assert post.to_xml =~ %r(<content>bar</content>)
102
+ end
103
+
104
+ test "to_xml doesn't affect untranslated models" do
105
+ blog = Blog.create(:description => "my blog")
106
+ blog.reload
107
+ assert blog.to_xml =~ %r(<description>my blog</description>)
108
+ end
109
+
110
+ test "translated_locales returns locales that have translations" do
111
+ first = Post.create!(:title => 'title', :locale => :en)
112
+ first.update_attributes(:title => 'Title', :locale => :de)
113
+
114
+ second = Post.create!(:title => 'title', :locale => :en)
115
+ second.update_attributes(:title => 'titre', :locale => :fr)
116
+
117
+ assert_equal [:de, :en, :fr], Post.translated_locales
118
+ assert_equal [:de, :en], first.translated_locales
119
+ assert_equal [:en, :fr], second.translated_locales
120
+
121
+ first.reload
122
+ second.reload
123
+
124
+ assert_equal [:de, :en], first.translated_locales
125
+ assert_equal [:en, :fr], second.translated_locales
126
+ end
127
+
128
+ test "a model with an after_save callback that reloads the model still saves correctly" do
129
+ reloading_post = ReloadingPost.create!(:title => 'title')
130
+ assert_equal 'title', reloading_post.title
131
+ assert_translated reloading_post, :en, :title, 'title'
132
+ end
133
+
134
+ test "with_translations eager loads translations" do
135
+ Post.create(:title => 'title 1')
136
+ Post.create(:title => 'title 2')
137
+
138
+ assert Post.with_translations.first.translations.loaded?
139
+ assert_equal ['title 1', 'title 2'], Post.with_translations.map(&:title)
140
+ end
141
+
142
+
143
+ test "a subclass of an untranslated model can translate attributes" do
144
+ post = Post.create(:title => 'title')
145
+ translated_comment = TranslatedComment.create(:post => post, :content => 'content')
146
+
147
+ assert_nothing_raised { translated_comment.translations }
148
+ assert_translated translated_comment, :en, :content, 'content'
149
+ end
150
+
151
+ test "modifiying translated attributes on a subclass of an untranslated model works" do
152
+ post = Post.create(:title => 'title')
153
+ translated_comment = TranslatedComment.create(:post => post, :content => 'content')
154
+
155
+ assert translated_comment.update_attributes(:content => 'Inhalt', :locale => :de)
156
+ assert_translated translated_comment, :en, :content, 'content'
157
+ assert_translated translated_comment, :de, :content, 'Inhalt'
158
+ end
159
+ end
@@ -0,0 +1,35 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+ require 'i18n/missing_translations_log_handler'
3
+
4
+ class TestLogger < String
5
+ def warn(msg) self.concat msg; end
6
+ end
7
+
8
+ class LogMissingTranslationsTest < Test::Unit::TestCase
9
+ def setup
10
+ @locale, @key, @options = :en, :foo, {}
11
+ @exception = I18n::MissingTranslationData.new(@locale, @key, @options)
12
+
13
+ @logger = TestLogger.new
14
+ I18n.missing_translations_logger = @logger
15
+ super
16
+ end
17
+
18
+ test "defines I18n.missing_translations_logger accessor" do
19
+ assert I18n.respond_to?(:missing_translations_logger)
20
+ end
21
+
22
+ test "defines I18n.missing_translations_logger= writer" do
23
+ assert I18n.respond_to?(:missing_translations_logger=)
24
+ end
25
+
26
+ test "still returns the exception message for MissingTranslationData exceptions" do
27
+ result = I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
28
+ assert_match /translation missing: en(\W+)foo/, result
29
+ end
30
+
31
+ test "logs the missing translation to I18n.missing_translations_logger" do
32
+ I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
33
+ assert_match /translation missing: en(\W+)foo/, @logger
34
+ end
35
+ end
@@ -0,0 +1,105 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+ require 'logger'
6
+
7
+ Bundler.require(:default, :test)
8
+ require 'database_cleaner'
9
+ require 'test_declarative'
10
+
11
+ log = '/tmp/globalize3_test.log'
12
+ FileUtils.touch(log) unless File.exists?(log)
13
+ ActiveRecord::Base.extend(Globalize::ActiveRecord::ActMacro)
14
+ ActiveRecord::Base.logger = Logger.new(log)
15
+ ActiveRecord::LogSubscriber.attach_to(:active_record)
16
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
17
+
18
+ $:.unshift File.expand_path('../../lib', __FILE__)
19
+ require 'globalize'
20
+ # require 'globalize/versioning/vestal_versions'
21
+ require 'erb'
22
+
23
+ require File.expand_path('../data/schema', __FILE__)
24
+ require File.expand_path('../data/models', __FILE__)
25
+
26
+ DatabaseCleaner.strategy = :truncation
27
+
28
+ class Test::Unit::TestCase
29
+ def setup
30
+ I18n.locale = :en
31
+ Globalize.locale = nil
32
+ DatabaseCleaner.start
33
+ end
34
+
35
+ def teardown
36
+ DatabaseCleaner.clean
37
+ end
38
+
39
+ def with_locale(*args, &block)
40
+ Globalize.with_locale(*args, &block)
41
+ end
42
+
43
+ def with_fallbacks
44
+ previous = I18n.backend
45
+ I18n.backend = BackendWithFallbacks.new
46
+ I18n.pretend_fallbacks
47
+ return yield
48
+ ensure
49
+ I18n.hide_fallbacks
50
+ I18n.backend = previous
51
+ end
52
+
53
+ def assert_included(item, array)
54
+ assert_block "Item #{item.inspect} is not included in the array #{array.inspect}" do
55
+ array.include?(item)
56
+ end
57
+ end
58
+
59
+ def assert_belongs_to(model, other)
60
+ assert_association(model, :belongs_to, other)
61
+ end
62
+
63
+ def assert_has_many(model, other)
64
+ assert_association(model, :has_many, other)
65
+ end
66
+
67
+ def assert_association(model, type, other)
68
+ assert model.reflect_on_all_associations(type).any? { |a| a.name.to_s == other.to_s }
69
+ end
70
+
71
+ def assert_translated(record, locale, attributes, translations)
72
+ assert_equal Array.wrap(translations), Array.wrap(attributes).map { |name| record.send(name, locale) }
73
+ end
74
+ end
75
+
76
+ ActiveRecord::Base.class_eval do
77
+ class << self
78
+ def index_exists?(index_name)
79
+ connection.indexes(table_name).any? { |index| index.name == index_name.to_s }
80
+ end
81
+
82
+ def index_exists_on?(column_name)
83
+ connection.indexes(table_name).any? { |index| index.columns == [column_name.to_s] }
84
+ end
85
+ end
86
+ end
87
+
88
+ class BackendWithFallbacks < I18n::Backend::Simple
89
+ include I18n::Backend::Fallbacks
90
+ end
91
+
92
+ meta = class << I18n; self; end
93
+ meta.class_eval do
94
+ alias_method(:alternatives, :fallbacks)
95
+
96
+ def pretend_fallbacks
97
+ class << I18n; self; end.send(:alias_method, :fallbacks, :alternatives)
98
+ end
99
+
100
+ def hide_fallbacks
101
+ class << I18n; self; end.send(:remove_method, :fallbacks)
102
+ end
103
+ end
104
+
105
+ I18n.hide_fallbacks