translatable_ar 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,7 @@
1
+ require 'active_support'
2
+ if defined? Rails
3
+ require "railtie"
4
+ end
5
+ require 'translatable_ar_class'
6
+ require 'active_record_methods'
7
+ ActiveRecord::Base.send(:include, TranslatableAR::ActiveRecordMethods)
@@ -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