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,108 @@
1
+ ActiveRecord::Migration.verbose = false
2
+
3
+ ActiveRecord::Schema.define do
4
+ create_table :translations, :force => true do |t|
5
+ t.string :blah
6
+ end
7
+
8
+ create_table :blogs, :force => true do |t|
9
+ t.string :description
10
+ end
11
+
12
+ create_table :posts, :force => true do |t|
13
+ t.references :blog
14
+ t.boolean :published
15
+ end
16
+
17
+ create_table :post_translations, :force => true do |t|
18
+ t.string :locale
19
+ t.references :post
20
+ t.string :title
21
+ t.text :content
22
+ t.boolean :published
23
+ t.datetime :published_at
24
+ end
25
+
26
+ create_table :parents, :force => true do |t|
27
+ end
28
+
29
+ create_table :parent_translations, :force => true do |t|
30
+ t.string :locale
31
+ t.references :parent
32
+ t.text :content
33
+ t.string :type
34
+ end
35
+
36
+ create_table :comments, :force => true do |t|
37
+ t.references :post
38
+ end
39
+
40
+ create_table :comment_translations, :force => true do |t|
41
+ t.string :locale
42
+ t.references :comment
43
+ t.string :title
44
+ t.text :content
45
+ end
46
+
47
+ create_table :migrateds, :force => true do |t|
48
+ t.string :name
49
+ t.string :untranslated
50
+ end
51
+
52
+ create_table :untranslateds, :force => true do |t|
53
+ t.string :name
54
+ end
55
+
56
+ create_table :validatees, :force => true do |t|
57
+ end
58
+
59
+ create_table :validatee_translations, :force => true do |t|
60
+ t.references :validatee
61
+ t.string :locale
62
+ t.string :string
63
+ end
64
+
65
+ create_table :users, :force => true do |t|
66
+ t.string :email
67
+ t.datetime :created_at
68
+ end
69
+
70
+ create_table :user_translations, :force => true do |t|
71
+ t.references :user
72
+ t.string :locale
73
+ t.string :name
74
+ end
75
+
76
+ create_table :tasks, :force => true do |t|
77
+ t.string :name
78
+ t.datetime :created_at
79
+ end
80
+
81
+ create_table :task_translations, :force => true do |t|
82
+ t.references :task
83
+ t.string :locale
84
+ t.string :name
85
+ end
86
+
87
+ create_table "versions", :force => true do |t|
88
+ t.string "item_type", :null => false
89
+ t.integer "item_id", :null => false
90
+ t.string "event", :null => false
91
+ t.string "whodunnit"
92
+ t.text "object"
93
+ t.string "locale"
94
+ t.datetime "created_at"
95
+ end
96
+
97
+ add_index "versions", ["item_type", "item_id"], :name => "index_versions_on_item_type_and_item_id"
98
+
99
+ create_table 'UPPERCASE_TABLE_NAME', :force => true do |t|
100
+ t.string :name
101
+ end
102
+
103
+ create_table :UPPERCASE_TABLE_NAME_translations, :force => true do |t|
104
+ t.integer 'UPPERCASE_TABLE_NAME_id'
105
+ t.string :locale
106
+ t.string :name
107
+ end
108
+ end
@@ -0,0 +1,133 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ class AttributesTest < Test::Unit::TestCase
6
+ test 'defines accessors for the translated attributes' do
7
+ post = Post.new
8
+ assert post.respond_to?(:title)
9
+ assert post.respond_to?(:title=)
10
+ end
11
+
12
+ test "attribute_names returns translated and regular attribute names" do
13
+ assert_equal %w(blog_id content title), Post.new.attribute_names.sort & %w(blog_id content title)
14
+ end
15
+
16
+ test "attributes returns translated and regular attributes" do
17
+ post = Post.create(:title => 'foo')
18
+ attributes = post.attributes.slice('id', 'blog_id', 'title', 'content')
19
+ assert_equal({ 'id' => post.id, 'blog_id' => nil, 'title' => 'foo', 'content' => nil }, attributes)
20
+ end
21
+
22
+ test "write_attribute for non-translated attributes should return the value" do
23
+ user = User.create(:name => 'Max Mustermann', :email => 'max@mustermann.de')
24
+ new_email = 'm.muster@mann.de'
25
+ assert_equal new_email, user.write_attribute('email', new_email)
26
+ end
27
+
28
+ test 'translated_attribute_names returns translated attribute names' do
29
+ assert_equal [:title, :content], Post.translated_attribute_names & [:title, :content]
30
+ end
31
+
32
+ test "a translated attribute writer returns its argument" do
33
+ assert_equal 'foo', Post.new.title = 'foo'
34
+ end
35
+
36
+ test "a translated attribute reader returns the correct translation for a saved record after locale switching" do
37
+ post = Post.create(:title => 'title')
38
+ post.update_attributes(:title => 'Titel', :locale => :de)
39
+ post.reload
40
+
41
+ assert_translated post, :en, :title, 'title'
42
+ assert_translated post, :de, :title, 'Titel'
43
+ end
44
+
45
+ test "a translated attribute reader returns the correct translation for an unsaved record after locale switching" do
46
+ post = Post.create(:title => 'title')
47
+ with_locale(:de) { post.title = 'Titel' }
48
+
49
+ assert_translated post, :en, :title, 'title'
50
+ assert_translated post, :de, :title, 'Titel'
51
+ end
52
+
53
+ test "a translated attribute reader returns the correct translation for both saved/unsaved records while switching locales" do
54
+ post = Post.new(:title => 'title')
55
+ with_locale(:de) { post.title = 'Titel' }
56
+ with_locale(:he) { post.title = 'שם' }
57
+
58
+ assert_translated post, :de, :title, 'Titel'
59
+ assert_translated post, :he, :title, 'שם'
60
+ assert_translated post, :en, :title, 'title'
61
+ assert_translated post, :he, :title, 'שם'
62
+ assert_translated post, :de, :title, 'Titel'
63
+
64
+ post.save
65
+ post.reload
66
+
67
+ assert_translated post, :de, :title, 'Titel'
68
+ assert_translated post, :he, :title, 'שם'
69
+ assert_translated post, :en, :title, 'title'
70
+ assert_translated post, :he, :title, 'שם'
71
+ assert_translated post, :de, :title, 'Titel'
72
+ end
73
+
74
+ test "a translated attribute reader returns nil if no translations are found on an unsaved record" do
75
+ post = Post.new(:title => 'foo')
76
+ assert_equal 'foo', post.title
77
+ assert_nil post.content
78
+ end
79
+
80
+ test "a translated attribute reader returns nil if no translations are found on a saved record" do
81
+ post = Post.create(:title => 'foo')
82
+ post.reload
83
+ assert_equal 'foo', post.title
84
+ assert_nil post.content
85
+ end
86
+
87
+ test "before_type_cast reader works for translated attributes" do
88
+ post = Post.create(:title => 'title')
89
+ post.update_attributes(:title => "Titel", :locale => :de)
90
+
91
+ with_locale(:en) { assert_equal 'title', post.title_before_type_cast }
92
+ with_locale(:de) { assert_equal 'Titel', post.title_before_type_cast }
93
+ end
94
+
95
+ test "saves all translations on an sti model after locale switching" do
96
+ child = Child.new(:content => 'foo')
97
+ with_locale(:de) { child.content = 'bar' }
98
+ with_locale(:he) { child.content = 'baz' }
99
+ child.save
100
+ child.reload
101
+
102
+ assert_translated child, :en, :content, 'foo'
103
+ assert_translated child, :de, :content, 'bar'
104
+ assert_translated child, :he, :content, 'baz'
105
+ end
106
+
107
+ test 'attribute reader without arguments will use the current locale on Globalize or I18n' do
108
+ with_locale(:de) do
109
+ Post.create!(:title => 'Titel', :content => 'Inhalt')
110
+ end
111
+ I18n.locale = :de
112
+ assert_equal 'Titel', Post.first.title
113
+
114
+ I18n.locale = :en
115
+ Globalize.locale = :de
116
+ assert_equal 'Titel', Post.first.title
117
+ end
118
+
119
+ test 'attribute reader when passed a locale will use the given locale' do
120
+ post = with_locale(:de) do
121
+ Post.create!(:title => 'Titel', :content => 'Inhalt')
122
+ end
123
+ assert_equal 'Titel', post.title(:de)
124
+ end
125
+
126
+ test 'modifying a translated attribute does not change the untranslated value' do
127
+ post = Post.create(:title => 'title')
128
+ before = post.untranslated_attributes['title']
129
+ post.title = 'changed title'
130
+ assert_equal post.untranslated_attributes['title'], before
131
+ end
132
+
133
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ class CloneTest < Test::Unit::TestCase
6
+
7
+ test 'stores translations from clonned new record' do
8
+ check_stored_translations(standard_post.clone)
9
+ end
10
+
11
+ test 'stores translations from clonned created record' do
12
+ clonned = saved_post.clone
13
+ check_stored_translations(clonned)
14
+ end
15
+
16
+ test 'stores translations from clonned found record' do
17
+ check_stored_translations( Post.find(saved_post).clone )
18
+ end
19
+
20
+ test 'stores translations from clonned reloaded after creation record' do
21
+ check_stored_translations(saved_post.reload.clone)
22
+ end
23
+
24
+
25
+ private
26
+
27
+
28
+ def standard_post
29
+ p = Post.new({:title => 'title', :content => 'content'})
30
+ with_locale(:he) { p.title= 'שם' }
31
+ return p
32
+ end
33
+
34
+ def saved_post
35
+ standard_post.tap{|p| p.save!}
36
+ end
37
+
38
+ def translations_modifications(clonned)
39
+ clonned.content = 'another content'
40
+ with_locale(:de) { clonned.title = 'Titel' }
41
+ end
42
+
43
+ def translations_assertions(clonned)
44
+ assert_translated clonned, :en, :title, 'title' # original
45
+ assert_translated clonned, :en, :content, 'another content' # changed
46
+ assert_translated clonned, :de, :title, 'Titel' # new
47
+ assert_translated clonned, :he, :title, 'שם' # untouched language
48
+ end
49
+
50
+ def check_stored_translations(clonned)
51
+ translations_modifications(clonned)
52
+ translations_assertions(clonned)
53
+ clonned.save!
54
+ clonned.reload
55
+ translations_assertions(clonned)
56
+ end
57
+
58
+ end
@@ -0,0 +1,61 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class DirtyTrackingTest < Test::Unit::TestCase
4
+ test "dirty tracking works" do
5
+ post = Post.create(:title => 'title', :content => 'content')
6
+ assert_equal [], post.changed
7
+
8
+ post.title = 'changed title'
9
+ assert_equal ['title'], post.changed
10
+
11
+ post.content = 'changed content'
12
+ assert_included 'title', post.changed
13
+ assert_included 'content', post.changed
14
+ end
15
+
16
+ test 'dirty tracking works per a locale' do
17
+ post = Post.create(:title => 'title', :content => 'content')
18
+ assert_equal [], post.changed
19
+
20
+ post.title = 'changed title'
21
+ assert_equal({ 'title' => ['title', 'changed title'] }, post.changes)
22
+ post.save
23
+
24
+ I18n.locale = :de
25
+ assert_equal nil, post.title
26
+
27
+ post.title = 'Titel'
28
+ assert_equal({ 'title' => [nil, 'Titel'] }, post.changes)
29
+ end
30
+
31
+ # ummm ... is this actually desired behaviour? probably depends on how we use it
32
+ test 'dirty tracking works after locale switching' do
33
+ post = Post.create(:title => 'title', :content => 'content')
34
+ assert_equal [], post.changed
35
+
36
+ post.title = 'changed title'
37
+ I18n.locale = :de
38
+ assert_equal ['title'], post.changed
39
+ end
40
+
41
+ test 'dirty tracking works on sti model' do
42
+ child = Child.create(:content => 'foo')
43
+ assert_equal [], child.changed
44
+
45
+ child.content = 'bar'
46
+ assert_equal ['content'], child.changed
47
+
48
+ child.content = 'baz'
49
+ assert_included 'content', child.changed
50
+ end
51
+
52
+ test 'dirty tracking works on sti model after locale switching' do
53
+ child = Child.create(:content => 'foo')
54
+ assert_equal [], child.changed
55
+
56
+ child.content = 'bar'
57
+ I18n.locale = :de
58
+
59
+ assert_equal ['content'], child.changed
60
+ end
61
+ end
@@ -0,0 +1,171 @@
1
+ # encoding: utf-8
2
+
3
+ require File.expand_path('../../test_helper', __FILE__)
4
+
5
+ class DynamicFindersTest < Test::Unit::TestCase
6
+
7
+ test "Does not break normal finders" do
8
+ user = User.create!(:name => "name", :email => "email@example.org")
9
+
10
+ assert_equal user, User.find_by_email(user.email)
11
+ assert_equal [user], User.find_all_by_email([user.email])
12
+
13
+ assert_equal user, User.find_by_id_and_created_at(user.id, user.created_at)
14
+ assert_equal user, User.find_by_id_and_email(user.id, user.email)
15
+
16
+ assert_equal [user], User.find_all_by_id_and_created_at(user.id, user.created_at)
17
+ assert_equal [user], User.find_all_by_id_and_email(user.id, user.email)
18
+ end
19
+
20
+ test "simple dynamic finders do work" do
21
+ foo = Post.create!(:title => 'foo')
22
+ bar = Post.create!(:title => 'bar')
23
+
24
+ assert_equal foo, Post.find_by_title('foo')
25
+ assert_equal bar, Post.find_by_title('bar')
26
+ assert_nil Post.find_by_title("non existing")
27
+
28
+ assert_equal [foo], Post.find_all_by_title('foo')
29
+ assert_equal [], Post.find_all_by_title('non existing')
30
+ end
31
+
32
+ # https://github.com/svenfuchs/globalize3/issues#issue/5
33
+ test "simple dynamic finders retruns results from current locale and fallbacks" do
34
+ en, de, he = 'title', 'titel', 'שם'
35
+ post = Post.create!(:title => en)
36
+ post.update_attributes!(:title => de, :locale => :de)
37
+ post.update_attributes!(:title => he, :locale => :he)
38
+
39
+ with_fallbacks do
40
+ I18n.fallbacks = {:de => [:de, :en], :he => [:he, :en], :en => [:en]}
41
+
42
+ with_locale(:en) do
43
+ assert Post.find_by_title(en)
44
+ assert_nil Post.find_by_title(de)
45
+ end
46
+
47
+ with_locale(:de) do
48
+ assert Post.find_by_title(en)
49
+ assert Post.find_by_title(de)
50
+ assert_nil Post.find_by_title(he)
51
+ end
52
+
53
+ with_locale(:he) do
54
+ assert Post.find_by_title(en)
55
+ assert Post.find_by_title(he)
56
+ assert_nil Post.find_by_title(de)
57
+ end
58
+
59
+ I18n.fallbacks.clear
60
+ end
61
+ end
62
+
63
+ test "simple dynamic finders do work on sti models" do
64
+ Child.create(:content => 'foo')
65
+ Child.create(:content => 'bar')
66
+
67
+ assert_equal 'foo', Child.find_by_content('foo').content
68
+ assert_equal 'foo', Child.find_all_by_content('foo').first.content
69
+ end
70
+
71
+ test "records returned by dynamic finders have writable attributes" do
72
+ Post.create(:title => 'original')
73
+ post = Post.find_by_title('original')
74
+ post.title = 'modified'
75
+ assert_nothing_raised(ActiveRecord::ReadOnlyRecord) do
76
+ post.save
77
+ end
78
+ end
79
+
80
+ test "responds to possible dynamic finders" do
81
+ assert Post.respond_to?(:find_by_title)
82
+ assert Post.respond_to?(:find_all_by_title)
83
+ assert Post.respond_to?(:find_by_title_and_content)
84
+ assert Post.respond_to?(:find_all_by_title_and_content)
85
+ end
86
+
87
+ test "does not responds to impossible dynamic finders" do
88
+ assert ! Post.respond_to?(:find_by_foo)
89
+ assert ! Post.respond_to?(:find_all_by_foo)
90
+ assert ! Post.respond_to?(:find_by_title_and_foo)
91
+ assert ! Post.respond_to?(:find_all_by_title_and_foo)
92
+ end
93
+
94
+ end
95
+
96
+ class TwoTranslatedAttributesDynamicFindersTest < Test::Unit::TestCase
97
+
98
+ def setup
99
+ @title1, @title2, @content = "n1", "n2", "desc"
100
+ @p1 = Post.create!(:title => @title1, :content => @content)
101
+ @p2 = Post.create!(:title => @title2, :content => @content)
102
+ end
103
+
104
+ test "find one element by two translation columns" do
105
+ assert_equal @p1, Post.find_by_title_and_content(@title1, @content)
106
+ assert_equal @p2, Post.find_by_content_and_title(@content, @title2)
107
+ end
108
+
109
+ test "return nil for none existing values" do
110
+ assert_nil Post.find_by_content_and_title(@content, "not exisiting")
111
+ assert_nil Post.find_by_content_and_title("not existing", @title2)
112
+
113
+ assert_nil Post.find_by_title_and_content("not exisiting", @content)
114
+ assert_nil Post.find_by_title_and_content(@title2, "not existing")
115
+ end
116
+
117
+ test "find elements by two translation columns" do
118
+ two_results = Post.find_all_by_title_and_content([@title1, @title2], @content)
119
+ assert two_results.include?(@p1)
120
+ assert two_results.include?(@p2)
121
+
122
+ assert_equal [@p2], Post.find_all_by_content_and_title(@content, @title2)
123
+ end
124
+
125
+ test "returns empty result set for none existing values" do
126
+ assert_equal [], Post.find_all_by_title_and_content([@title1, @title2], "not existing")
127
+ assert_equal [], Post.find_all_by_title_and_content("not existing", @content)
128
+
129
+ assert_equal [], Post.find_all_by_content_and_title(["not existing"], @title1)
130
+ assert_equal [], Post.find_all_by_content_and_title(@content, ["not existing"])
131
+ end
132
+
133
+ end
134
+
135
+ class TranslatedAndNormalAttributeDynamicFindersTest < Test::Unit::TestCase
136
+
137
+ def setup
138
+ @name1, @name2, @email = "n1", "n2", "email@example.org"
139
+ @p1 = User.create!(:name => @name1, :email => @email)
140
+ @p2 = User.create!(:name => @name2, :email => @email)
141
+ end
142
+
143
+ test "find one element by two columns" do
144
+ assert_equal @p1, User.find_by_name_and_email(@name1, @email)
145
+ assert_equal @p2, User.find_by_email_and_name(@email, @name2)
146
+ end
147
+
148
+ test "return nil for none existing values" do
149
+ assert_nil User.find_by_email_and_name(@email, "not exisiting")
150
+ assert_nil User.find_by_email_and_name("not existing", @name2)
151
+
152
+ assert_nil User.find_by_name_and_email("not exisiting", @email)
153
+ assert_nil User.find_by_name_and_email(@name2, "not existing")
154
+ end
155
+
156
+ test "find elements by two translation columns" do
157
+ two_results = User.find_all_by_name_and_email([@name1, @name2], @email)
158
+ assert two_results.include?(@p1)
159
+ assert two_results.include?(@p2)
160
+ assert_equal [@p2], User.find_all_by_email_and_name(@email, @name2)
161
+ end
162
+
163
+ test "returns empty result set for none existing values" do
164
+ assert_equal [], User.find_all_by_name_and_email([@name1, @name2], "not existing")
165
+ assert_equal [], User.find_all_by_name_and_email("not existing", @email)
166
+
167
+ assert_equal [], User.find_all_by_email_and_name(["not existing"], @name1)
168
+ assert_equal [], User.find_all_by_email_and_name(@email, ["not existing"])
169
+ end
170
+
171
+ end