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,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