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.
@@ -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