translatable_ar 1.0.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.
- data/lib/active_record_methods.rb +65 -0
- data/lib/railtie.rb +39 -0
- data/lib/templates/hstore.rb +72 -0
- data/lib/templates/text.rb +39 -0
- data/lib/translatable_ar.rb +7 -0
- data/lib/translatable_ar_class.rb +16 -0
- data/test/rails_3.2.11/test/unit/product_test.rb +110 -0
- metadata +70 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
class TranslatableAR
|
2
|
+
module ActiveRecordMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
attr_accessor :translatable_ar_attributes
|
8
|
+
|
9
|
+
def translates(*attributes)
|
10
|
+
remember_translatable_ar_model
|
11
|
+
attributes.each do |attribute|
|
12
|
+
remember_translatable_ar_attribute(attribute)
|
13
|
+
define_translatable_ar_methods(attribute)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def remember_translatable_ar_model
|
20
|
+
TranslatableAR.models << self
|
21
|
+
TranslatableAR.models.uniq!
|
22
|
+
end
|
23
|
+
|
24
|
+
def remember_translatable_ar_attribute(attribute)
|
25
|
+
@translatable_ar_attributes ||= []
|
26
|
+
@translatable_ar_attributes << attribute
|
27
|
+
@translatable_ar_attributes.uniq!
|
28
|
+
end
|
29
|
+
|
30
|
+
def define_translatable_ar_methods(attribute)
|
31
|
+
serialize attribute, TranslatableAR.coder
|
32
|
+
class_eval do
|
33
|
+
define_translatable_ar_collection(attribute)
|
34
|
+
define_translatable_ar_writer(attribute)
|
35
|
+
define_translatable_ar_reader(attribute)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def define_translatable_ar_collection(attribute)
|
40
|
+
define_method("#{attribute}_translations") do
|
41
|
+
self["#{attribute}"] ||= {}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def define_translatable_ar_reader(attribute)
|
46
|
+
define_method("#{attribute}") do
|
47
|
+
send("#{attribute}_translations")[I18n.locale.to_s]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def define_translatable_ar_writer(attribute)
|
52
|
+
define_method("#{attribute}=") do |value|
|
53
|
+
if value.is_a?(Hash)
|
54
|
+
translations = value
|
55
|
+
else
|
56
|
+
translations = send("#{attribute}_translations")
|
57
|
+
translations[I18n.locale.to_s] = value
|
58
|
+
end
|
59
|
+
super(translations)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/railtie.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'rails/generators'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
class TranslatableAr < Rails::Railtie
|
6
|
+
|
7
|
+
initializer 'translatable_ar' do
|
8
|
+
ActiveSupport.on_load :active_record do
|
9
|
+
require "translatable_ar"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Setup < Rails::Generators::Base
|
14
|
+
include Rails::Generators::Migration
|
15
|
+
|
16
|
+
def self.source_root
|
17
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.next_migration_number(dirname)
|
21
|
+
if ActiveRecord::Base.timestamped_migrations
|
22
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
23
|
+
else
|
24
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_migration_file
|
29
|
+
Rails.application.eager_load! # load models
|
30
|
+
migration_template template_path, 'db/migrate/setup_translatable_ar.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
def template_path
|
34
|
+
TranslatableAR.uses_hstore? ? 'hstore.rb' : 'text.rb'
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
class SetupTranslatableAr < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def up
|
4
|
+
<% TranslatableAR.models.each do |model| -%>
|
5
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
6
|
+
add_column :<%= model.table_name %>, :<%= attribute %>_i18n_temp, :hstore
|
7
|
+
<% end -%>
|
8
|
+
<% end -%>
|
9
|
+
<% TranslatableAR.models.each do |model| -%>
|
10
|
+
|
11
|
+
<%= model.to_s %>.translates <%= model.translatable_ar_attributes.map {|a| ":#{a}_i18n_temp" }.join(', ') %>
|
12
|
+
<%= model.to_s %>.all.each do |object|
|
13
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
14
|
+
if object[:<%=attribute%>].is_a? Hash
|
15
|
+
object.<%=attribute%>_i18n_temp = object[:<%=attribute%>]
|
16
|
+
else
|
17
|
+
object.<%=attribute%>_i18n_temp = {'<%= I18n.locale%>' => object[:<%=attribute%>]}
|
18
|
+
end
|
19
|
+
<% end -%>
|
20
|
+
object.save if object.changed?
|
21
|
+
end
|
22
|
+
<% end -%>
|
23
|
+
<% TranslatableAR.models.each do |model| -%>
|
24
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
25
|
+
remove_column :<%= model.table_name %>, :<%= attribute %>
|
26
|
+
rename_column :<%= model.table_name %>, :<%= attribute%>_i18n_temp, :<%= attribute %>
|
27
|
+
<% end -%>
|
28
|
+
<% end -%>
|
29
|
+
|
30
|
+
<% TranslatableAR.models.each do |model| -%>
|
31
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
32
|
+
# execute "CREATE INDEX <%= model.table_name%>_gin_<%= attribute %> ON <%= model.table_name %> USING GIN(<%= attribute %>);"
|
33
|
+
<% end -%>
|
34
|
+
<% end -%>
|
35
|
+
end
|
36
|
+
|
37
|
+
def down
|
38
|
+
<% TranslatableAR.models.each do |model| -%>
|
39
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
40
|
+
add_column :<%= model.table_name %>, :<%= attribute %>_i18n_temp, :<%= model.columns_hash[attribute.to_s].type %>
|
41
|
+
<% end -%>
|
42
|
+
<% end -%>
|
43
|
+
<% TranslatableAR.models.each do |model| -%>
|
44
|
+
|
45
|
+
<%= model.to_s %>.translates <%= model.translatable_ar_attributes.map {|a| ":#{a}_i18n_temp" }.join(', ') %>
|
46
|
+
<%= model.to_s %>.all.each do |object|
|
47
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
48
|
+
if object[:<%=attribute%>].is_a? Hash
|
49
|
+
object.<%=attribute%>_i18n_temp = object[:<%=attribute%>]['<%= I18n.locale%>']
|
50
|
+
else
|
51
|
+
object.<%=attribute%>_i18n_temp = object[:<%=attribute%>]
|
52
|
+
end
|
53
|
+
<% end -%>
|
54
|
+
object.save if object.changed?
|
55
|
+
end
|
56
|
+
<% end -%>
|
57
|
+
|
58
|
+
<% TranslatableAR.models.each do |model| -%>
|
59
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
60
|
+
execute "DROP INDEX <%= model.table_name%>_gin_<%= attribute %>;"
|
61
|
+
<% end -%>
|
62
|
+
<% end -%>
|
63
|
+
|
64
|
+
<% TranslatableAR.models.each do |model| -%>
|
65
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
66
|
+
remove_column :<%= model.table_name %>, :<%= attribute %>
|
67
|
+
rename_column :<%= model.table_name %>, :<%= attribute%>_i18n_temp, :<%= attribute %>
|
68
|
+
<% end -%>
|
69
|
+
<% end -%>
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class SetupTranslatableAr < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def up
|
4
|
+
<% TranslatableAR.models.each do |model| -%>
|
5
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
6
|
+
add_column :<%= model.table_name %>, :<%= attribute %>_i18n_temp, :text
|
7
|
+
<% end -%>
|
8
|
+
<% end -%>
|
9
|
+
<% TranslatableAR.models.each do |model| -%>
|
10
|
+
|
11
|
+
<%= model.to_s %>.translates <%= model.translatable_ar_attributes.map {|a| ":#{a}_i18n_temp" }.join(', ') %>
|
12
|
+
<%= model.to_s %>.all.each do |object|
|
13
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
14
|
+
if object[:<%=attribute%>].is_a? Hash
|
15
|
+
object.<%=attribute%>_i18n_temp = object[:<%=attribute%>]
|
16
|
+
else
|
17
|
+
object.<%=attribute%>_i18n_temp = {'<%= I18n.locale%>' => object[:<%=attribute%>]}
|
18
|
+
end
|
19
|
+
<% end -%>
|
20
|
+
object.save if object.changed?
|
21
|
+
end
|
22
|
+
<% end -%>
|
23
|
+
<% TranslatableAR.models.each do |model| -%>
|
24
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
25
|
+
remove_column :<%= model.table_name %>, :<%= attribute %>
|
26
|
+
rename_column :<%= model.table_name %>, :<%= attribute%>_i18n_temp, :<%= attribute %>
|
27
|
+
<% end -%>
|
28
|
+
<% end -%>
|
29
|
+
end
|
30
|
+
|
31
|
+
def down
|
32
|
+
<% TranslatableAR.models.each do |model| -%>
|
33
|
+
<% model.translatable_ar_attributes.each do |attribute| -%>
|
34
|
+
change_column :<%= model.table_name %>, :<%= attribute %>, :<%= model.columns_hash[attribute.to_s].type %>
|
35
|
+
<% end -%>
|
36
|
+
<% end -%>
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class TranslatableAR
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_accessor :models, :coder
|
5
|
+
def coder
|
6
|
+
@coder ||= Object # Use ActiveRecord's default serializer class
|
7
|
+
end
|
8
|
+
def models
|
9
|
+
@models ||= []
|
10
|
+
end
|
11
|
+
def uses_hstore?
|
12
|
+
@coder == ActiveRecord::Coders::Hstore rescue false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class ProductTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
# Singular Association
|
6
|
+
|
7
|
+
test "build with nested locale values" do
|
8
|
+
p = Product.new(:name => {'en' => 'Chair', 'da' => 'Stol'}, image_url: '/image1.png')
|
9
|
+
assert_equal 'Chair', p.name_translations['en']
|
10
|
+
assert_equal 'Stol', p.name_translations['da']
|
11
|
+
assert_equal '/image1.png', p.image_url
|
12
|
+
end
|
13
|
+
|
14
|
+
test "build with default locale" do
|
15
|
+
p = Product.new(name: 'Table', image_url: '/image1.png')
|
16
|
+
assert_equal('Table', p.name)
|
17
|
+
assert_equal({'en' => 'Table'}, p.name_translations)
|
18
|
+
assert_equal '/image1.png', p.image_url
|
19
|
+
end
|
20
|
+
|
21
|
+
test "create with nested locale values" do
|
22
|
+
p = Product.create(:name => {'en' => 'Chair', 'da' => 'Stol'}, image_url: '/image1.png')
|
23
|
+
assert_equal 'Chair', p.name_translations['en']
|
24
|
+
assert_equal 'Stol', p.name_translations['da']
|
25
|
+
assert_equal '/image1.png', p.image_url
|
26
|
+
end
|
27
|
+
|
28
|
+
test "create with default locale" do
|
29
|
+
p = Product.create(name: 'Table', image_url: '/image1.png')
|
30
|
+
assert_equal 'Table', p.name
|
31
|
+
assert_equal({'en' => 'Table'}, p.name_translations)
|
32
|
+
assert_equal '/image1.png', p.image_url
|
33
|
+
end
|
34
|
+
|
35
|
+
test "update with nested locale values" do
|
36
|
+
p = Product.new
|
37
|
+
p.update_attributes(:name => {'en' => 'Chair', 'da' => 'Stol'}, image_url: '/image1.png')
|
38
|
+
assert_equal 'Chair', p.name_translations['en']
|
39
|
+
assert_equal 'Stol', p.name_translations['da']
|
40
|
+
assert_equal '/image1.png', p.image_url
|
41
|
+
end
|
42
|
+
|
43
|
+
test "update with locale" do
|
44
|
+
p = Product.new
|
45
|
+
p.update_attributes(name: 'Table', image_url: '/image1.png')
|
46
|
+
assert_equal 'Table', p.name
|
47
|
+
assert_equal({'en' => 'Table'}, p.name_translations)
|
48
|
+
assert_equal '/image1.png', p.image_url
|
49
|
+
end
|
50
|
+
|
51
|
+
test "update with different locale" do
|
52
|
+
p = Product.create(name: 'Table', image_url: '/image1.png')
|
53
|
+
|
54
|
+
assert_equal 'Table', p.name
|
55
|
+
assert_equal '/image1.png', p.image_url
|
56
|
+
|
57
|
+
I18n.locale = :da
|
58
|
+
|
59
|
+
assert_equal nil, p.name
|
60
|
+
assert_equal '/image1.png', p.image_url
|
61
|
+
|
62
|
+
p.name = 'Bord'
|
63
|
+
|
64
|
+
assert_equal 'Bord', p.name
|
65
|
+
assert_equal({'en' => 'Table', 'da' => 'Bord'}, p.name_translations)
|
66
|
+
|
67
|
+
p.save
|
68
|
+
|
69
|
+
I18n.locale = :en
|
70
|
+
|
71
|
+
assert_equal 'Table', p.name
|
72
|
+
assert_equal({'en' => 'Table', 'da' => 'Bord'}, p.name_translations)
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
# Collection Association
|
77
|
+
|
78
|
+
test "build from collection with nested locale values" do
|
79
|
+
c = Category.create(name: 'Furniture')
|
80
|
+
p = c.products.build(:name => {'en' => 'Chair', 'da' => 'Stol'}, image_url: '/image1.png')
|
81
|
+
assert_equal 'Chair', p.name_translations['en']
|
82
|
+
assert_equal 'Stol', p.name_translations['da']
|
83
|
+
assert_equal '/image1.png', p.image_url
|
84
|
+
end
|
85
|
+
|
86
|
+
test "build from collection with default locale" do
|
87
|
+
c = Category.create(name: 'Furniture')
|
88
|
+
p = c.products.build(name: 'Table', image_url: '/image1.png')
|
89
|
+
assert_equal('Table', p.name)
|
90
|
+
assert_equal({'en' => 'Table'}, p.name_translations)
|
91
|
+
assert_equal '/image1.png', p.image_url
|
92
|
+
end
|
93
|
+
|
94
|
+
test "create from collection with nested locale values" do
|
95
|
+
c = Category.create(name: 'Furniture')
|
96
|
+
p = c.products.create(:name => {'en' => 'Chair', 'da' => 'Stol'}, image_url: '/image1.png')
|
97
|
+
assert_equal 'Chair', p.name_translations['en']
|
98
|
+
assert_equal 'Stol', p.name_translations['da']
|
99
|
+
assert_equal '/image1.png', p.image_url
|
100
|
+
end
|
101
|
+
|
102
|
+
test "create from collection with default locale" do
|
103
|
+
c = Category.create(name: 'Furniture')
|
104
|
+
p = c.products.create(name: 'Table', image_url: '/image1.png')
|
105
|
+
assert_equal 'Table', p.name
|
106
|
+
assert_equal({'en' => 'Table'}, p.name_translations)
|
107
|
+
assert_equal '/image1.png', p.image_url
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: translatable_ar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kasper Bjørn Nielsen
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: No joins. No locale columns. Attribute translations are saved in-column
|
31
|
+
as a serialized hash.
|
32
|
+
email: kasperbn@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/active_record_methods.rb
|
38
|
+
- lib/railtie.rb
|
39
|
+
- lib/templates/hstore.rb
|
40
|
+
- lib/templates/text.rb
|
41
|
+
- lib/translatable_ar.rb
|
42
|
+
- lib/translatable_ar_class.rb
|
43
|
+
- test/rails_3.2.11/test/unit/product_test.rb
|
44
|
+
homepage: http://github.com/kasperbn/translatable_ar
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubyforge_project:
|
65
|
+
rubygems_version: 1.8.24
|
66
|
+
signing_key:
|
67
|
+
specification_version: 3
|
68
|
+
summary: Less painful i18n for your active record models
|
69
|
+
test_files:
|
70
|
+
- test/rails_3.2.11/test/unit/product_test.rb
|