activerecord_translatable 0.0.1 → 0.0.2
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.
- checksums.yaml +7 -0
- data/README.rdoc +42 -22
- data/lib/activerecord_translatable.rb +18 -1
- data/lib/activerecord_translatable/extension.rb +48 -20
- data/lib/version.rb +5 -0
- data/spec/activerecord_translateable_spec.rb +61 -4
- data/spec/dummy/app/models/foo.rb +2 -0
- data/spec/dummy/app/models/noarraything.rb +4 -0
- data/spec/dummy/app/models/something.rb +0 -2
- data/spec/dummy/config/database.yml +3 -3
- data/spec/dummy/db/migrate/20130124094113_create_foo.rb +7 -0
- data/spec/dummy/db/migrate/20130124100555_create_noarraythings.rb +9 -0
- data/spec/dummy/db/schema.rb +12 -1
- data/spec/dummy/log/development.log +373 -46
- data/spec/dummy/log/test.log +5991 -0
- metadata +36 -29
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d0a2bb1ba9aa75761f54be04bfddf1c8d981849
|
4
|
+
data.tar.gz: 48473b568dc9efb170db0432700b5edacee7b829
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec2b82cb27170bef276f5547e7a6c549118ef987c530cadad1941d42846fd019fc70847e1fa2f9c6e8fc2137e843830f8388839ce8035e3ae887087cc1799560
|
7
|
+
data.tar.gz: 4b063afdc3ab348cff5cdbd4127f24848d486a9e3e595af859ecd8214db5681d655716fd0c7ed4241bf8ec0aa16af34e2184e036662eb62577101ef5cd427e1c
|
data/README.rdoc
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
+
{<img src="https://travis-ci.org/sideshowcoder/activerecord_translatable.png" />}[https://travis-ci.org/sideshowcoder/activerecord_translatable]
|
2
|
+
|
1
3
|
= ActiveRecordTranslatable
|
2
4
|
|
3
5
|
Make attributes of an ActiveRecord Model translatable, and store the
|
4
6
|
translations in the provided I18n backend. This is really helpful if there
|
5
|
-
already is, a interface to provide ie missing translations for elements in I18n.
|
7
|
+
already is, a interface to provide ie missing translations for elements in I18n.
|
6
8
|
|
7
9
|
== Usage
|
10
|
+
Use inside of the model
|
8
11
|
|
9
|
-
class MyModel < ActiveRecord::Base
|
10
|
-
|
11
|
-
|
12
|
-
end
|
12
|
+
class MyModel < ActiveRecord::Base
|
13
|
+
translate :title
|
14
|
+
end
|
13
15
|
|
14
|
-
I18n.locale = :en
|
15
|
-
mymodel = MyModel.create(title: "My title", title_de: "Mein Title)
|
16
|
-
mymodel.title_wk = "Woohhaakkk"
|
16
|
+
I18n.locale = :en
|
17
|
+
mymodel = MyModel.create(title: "My title", title_de: "Mein Title)
|
18
|
+
mymodel.title_wk = "Woohhaakkk"
|
17
19
|
|
18
|
-
mymodel.title # => "My title"
|
19
|
-
mymodel.title_en # => "My title"
|
20
|
-
mymodel.title_wk # => "Woohhaakkk"
|
20
|
+
mymodel.title # => "My title"
|
21
|
+
mymodel.title_en # => "My title"
|
22
|
+
mymodel.title_wk # => "Woohhaakkk"
|
21
23
|
|
22
24
|
== Prerequisites
|
23
25
|
To save the locales ActiveRecordTranslatable saves an array of the specified locale, for
|
@@ -28,19 +30,37 @@ gem 'activerecord-postgres-array'
|
|
28
30
|
|
29
31
|
To add the array to the model the migration looks like this
|
30
32
|
|
31
|
-
class CreateMyModel < ActiveRecord::Migration
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
class CreateMyModel < ActiveRecord::Migration
|
34
|
+
def change
|
35
|
+
create_table :my_model do |t|
|
36
|
+
t.string_array :locales
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
If your database does not support native array types you can use ActiveRecords
|
42
|
+
ability to serialize the locales array
|
43
|
+
|
44
|
+
class MyModel < ActiveRecord::Base
|
45
|
+
serialize :locales
|
46
|
+
translate :name
|
47
|
+
end
|
48
|
+
|
49
|
+
And add the column as a string
|
50
|
+
|
51
|
+
class CreateMyModel < ActiveRecord::Migration
|
52
|
+
def change
|
53
|
+
create_table :my_model do |t|
|
54
|
+
t.string :locales
|
55
|
+
end
|
35
56
|
end
|
36
57
|
end
|
37
|
-
end
|
38
58
|
|
39
59
|
== How it works
|
40
60
|
|
41
|
-
Translateable saves the translation via I18n.backend.store_translations, this
|
42
|
-
means that the backend has to be able to store new items. So backend needs to
|
43
|
-
be for example the KeyValue or ActiveRecord one.
|
61
|
+
Translateable saves the translation via I18n.backend.store_translations, this
|
62
|
+
means that the backend has to be able to store new items. So backend needs to
|
63
|
+
be for example the KeyValue or ActiveRecord one.
|
44
64
|
More http://railscasts.com/episodes/256-i18n-backends
|
45
65
|
|
46
66
|
== Miscellaneous
|
@@ -51,7 +71,7 @@ Since the example is using postgres, to get the tests working there needs to be
|
|
51
71
|
a setup postgres user with the needed rights.
|
52
72
|
|
53
73
|
$ createuser -h localhost
|
54
|
-
Enter name of role to add: translatable
|
74
|
+
Enter name of role to add: translatable
|
55
75
|
Shall the new role be a superuser? (y/n) n
|
56
76
|
Shall the new role be allowed to create databases? (y/n) y
|
57
77
|
Shall the new role be allowed to create more new roles? (y/n) n
|
@@ -64,8 +84,8 @@ Afterwards the db needs to be setup
|
|
64
84
|
$ rake db:test:prepare
|
65
85
|
|
66
86
|
Now the test can be run via
|
67
|
-
|
68
|
-
$ rspec
|
87
|
+
|
88
|
+
$ rspec
|
69
89
|
|
70
90
|
=== Current State
|
71
91
|
The gem is still in early development and has just been extracted from a
|
@@ -1,9 +1,26 @@
|
|
1
1
|
require "active_record"
|
2
2
|
require "active_support/concern"
|
3
|
+
require "rails"
|
3
4
|
require "activerecord_translatable/extension"
|
4
5
|
|
5
6
|
module ActiveRecordTranslatable
|
6
7
|
|
7
|
-
|
8
|
+
class Railtie < Rails::Railtie
|
9
|
+
initializer "activerecord_translatable.load_into_active_record" do
|
10
|
+
ActiveSupport.on_load :active_record do
|
11
|
+
ActiveRecordTranslatable::Railtie.load
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Railtie
|
17
|
+
class << self
|
18
|
+
def load
|
19
|
+
if defined?(ActiveRecord)
|
20
|
+
ActiveRecord::Base.send(:include, ActiveRecordTranslatable)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
8
25
|
|
9
26
|
end
|
@@ -3,43 +3,63 @@ module ActiveRecordTranslatable
|
|
3
3
|
|
4
4
|
attr_accessor :translations
|
5
5
|
|
6
|
+
def translatable
|
7
|
+
self._translatable[base_name]
|
8
|
+
end
|
9
|
+
|
10
|
+
def available_locales
|
11
|
+
self.locales.map { |locale| locale.to_sym }
|
12
|
+
end
|
13
|
+
|
6
14
|
def setup_locale(locale)
|
7
15
|
locales = self.locales || []
|
8
16
|
locales << locale.to_s
|
9
17
|
self.locales = locales.uniq
|
10
|
-
@translations ||= {}
|
11
|
-
@translations[locale] ||= {}
|
12
18
|
end
|
13
19
|
|
14
|
-
def
|
15
|
-
|
20
|
+
def translations
|
21
|
+
@translations ||= Hash.new { |h,k| h[k] = {} }
|
22
|
+
end
|
23
|
+
|
24
|
+
def base_name
|
25
|
+
self.class.name.downcase
|
16
26
|
end
|
17
27
|
|
18
28
|
def translation(attribute, locale = I18n.locale)
|
19
|
-
setup_locale(locale)
|
20
29
|
begin
|
21
|
-
|
22
|
-
|
30
|
+
translation = translations.fetch(locale).fetch(attribute)
|
31
|
+
rescue KeyError
|
32
|
+
_get_stored_translation(attribute, locale)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _get_stored_translation(attribute, locale)
|
37
|
+
begin
|
38
|
+
translation = I18n.t("#{base_name}.#{attribute}-#{self.id}", locale: locale, raise: true)
|
39
|
+
setup_locale(locale)
|
40
|
+
translations[locale][attribute] = translation
|
23
41
|
rescue I18n::MissingTranslationData
|
24
|
-
|
42
|
+
nil
|
25
43
|
end
|
26
44
|
end
|
27
45
|
|
28
46
|
def set_translation(attribute, value, locale = I18n.locale)
|
29
47
|
setup_locale(locale)
|
30
|
-
|
48
|
+
translations[locale][attribute] = value
|
31
49
|
end
|
32
|
-
|
50
|
+
|
33
51
|
def write_translations
|
34
|
-
|
35
|
-
|
36
|
-
|
52
|
+
return if translations.empty? # there are no translations to be saved
|
53
|
+
|
54
|
+
translations.each do |locale, trans|
|
55
|
+
trans.each do |attribute, value|
|
56
|
+
I18n.backend.store_translations(locale, { "#{base_name}.#{attribute}-#{self.id}" => value }, escape: false)
|
37
57
|
end
|
38
58
|
end
|
39
59
|
end
|
40
60
|
|
41
61
|
def method_missing(method_name, *arguments, &block)
|
42
|
-
|
62
|
+
translatable.each do |attribute|
|
43
63
|
attribute = attribute.to_s
|
44
64
|
if method_name.to_s =~ /^#{attribute}_(.{2})=$/
|
45
65
|
return set_translation(attribute, arguments.first, $1.to_sym)
|
@@ -53,9 +73,9 @@ module ActiveRecordTranslatable
|
|
53
73
|
end
|
54
74
|
super
|
55
75
|
end
|
56
|
-
|
76
|
+
|
57
77
|
def respond_to_missing?(method_name, include_private = false)
|
58
|
-
|
78
|
+
translatable.each do |attribute|
|
59
79
|
attribute = attribute.to_s
|
60
80
|
if method_name.to_s =~ /^#{attribute}_(.{2})=$/
|
61
81
|
return true
|
@@ -72,14 +92,22 @@ module ActiveRecordTranslatable
|
|
72
92
|
|
73
93
|
included do
|
74
94
|
after_save :write_translations
|
75
|
-
cattr_accessor :
|
95
|
+
cattr_accessor :_translatable
|
76
96
|
end
|
77
97
|
|
78
98
|
module ClassMethods
|
79
99
|
def translate(*attributes)
|
80
|
-
self.
|
81
|
-
self.
|
82
|
-
|
100
|
+
self._translatable ||= Hash.new { |h,k| h[k] = [] }
|
101
|
+
self._translatable[base_name] = translatable.concat(attributes).uniq
|
102
|
+
end
|
103
|
+
|
104
|
+
def translatable
|
105
|
+
self._translatable[base_name] ||= []
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def base_name
|
110
|
+
self.name.downcase
|
83
111
|
end
|
84
112
|
end
|
85
113
|
|
data/lib/version.rb
ADDED
@@ -3,9 +3,21 @@ require 'spec_helper'
|
|
3
3
|
describe "ActiveRecordTranslateable" do
|
4
4
|
|
5
5
|
it "should add name to translateable" do
|
6
|
-
Something.
|
6
|
+
Something.translatable.should include(:name)
|
7
7
|
end
|
8
8
|
|
9
|
+
context "class without translations" do
|
10
|
+
it "should save" do
|
11
|
+
foo = Foo.new
|
12
|
+
foo.save.should be_true
|
13
|
+
end
|
14
|
+
it "should load" do
|
15
|
+
foo = Foo.create!
|
16
|
+
Foo.find(foo.id).should_not be_nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
9
21
|
context "translations" do
|
10
22
|
before(:each) do
|
11
23
|
@something = Something.create!(name: "Something")
|
@@ -37,7 +49,7 @@ describe "ActiveRecordTranslateable" do
|
|
37
49
|
@something.set_translation("name", "Etwas")
|
38
50
|
@something.translation("name", :de).should == "Etwas"
|
39
51
|
end
|
40
|
-
|
52
|
+
|
41
53
|
it "should write the stored translations to the backend" do
|
42
54
|
backend = double("Backend")
|
43
55
|
I18n.stub(:backend).and_return(backend)
|
@@ -46,6 +58,42 @@ describe "ActiveRecordTranslateable" do
|
|
46
58
|
@something.write_translations
|
47
59
|
end
|
48
60
|
|
61
|
+
it "should save the model without translations" do
|
62
|
+
something = Something.new
|
63
|
+
something.save.should be_true
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
context "trigger save on model change" do
|
69
|
+
before(:each) do
|
70
|
+
@backend = double("Backend")
|
71
|
+
I18n.stub(:backend).and_return(@backend)
|
72
|
+
end
|
73
|
+
it "should save translations on save" do
|
74
|
+
@backend.should_receive(:store_translations).twice
|
75
|
+
Something.new(name: 'something', name_de: 'etwas').save
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should save translations on create" do
|
79
|
+
@backend.should_receive(:store_translations).twice
|
80
|
+
Something.create(name: 'something', name_de: 'etwas')
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should save translations on update" do
|
84
|
+
@backend.should_receive(:store_translations).exactly(4)
|
85
|
+
sth = Something.create(name: 'something_old', name_de: 'etwas_old')
|
86
|
+
sth.update_attributes(name: 'something', name_de: 'etwas')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "don't set locales on read" do
|
91
|
+
let(:something) { Something.create!(name: "Something") }
|
92
|
+
|
93
|
+
it "should not include a read locale unless set to something" do
|
94
|
+
something.name_gr
|
95
|
+
something.locales.should_not include("gr")
|
96
|
+
end
|
49
97
|
end
|
50
98
|
|
51
99
|
context "custom created methods" do
|
@@ -67,7 +115,7 @@ describe "ActiveRecordTranslateable" do
|
|
67
115
|
|
68
116
|
end
|
69
117
|
|
70
|
-
context "locales" do
|
118
|
+
context "locales with db array support" do
|
71
119
|
let(:something) { Something.create!(name: "Something") }
|
72
120
|
|
73
121
|
it "should respond with available locales" do
|
@@ -75,7 +123,16 @@ describe "ActiveRecordTranslateable" do
|
|
75
123
|
something.name_de = "Etwas"
|
76
124
|
something.available_locales.should include(:en, :de)
|
77
125
|
end
|
78
|
-
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
context "locales without db array support" do
|
130
|
+
let(:thing) { Noarraything.create!(name: "thing", name_de: "ding") }
|
131
|
+
|
132
|
+
it "should store the locales as array" do
|
133
|
+
locales = thing.locales
|
134
|
+
thing.reload.locales.should == locales
|
135
|
+
end
|
79
136
|
end
|
80
137
|
|
81
138
|
end
|
@@ -3,7 +3,7 @@ development:
|
|
3
3
|
encoding: unicode
|
4
4
|
database: translatable_development
|
5
5
|
pool: 5
|
6
|
-
username: translatable
|
6
|
+
username: translatable
|
7
7
|
password:
|
8
8
|
host: localhost
|
9
9
|
|
@@ -15,7 +15,7 @@ test:
|
|
15
15
|
encoding: unicode
|
16
16
|
database: translatable_test
|
17
17
|
pool: 5
|
18
|
-
username:
|
18
|
+
username: postgres
|
19
19
|
password:
|
20
20
|
host: localhost
|
21
21
|
|
@@ -24,6 +24,6 @@ production:
|
|
24
24
|
encoding: unicode
|
25
25
|
database: translatable_production
|
26
26
|
pool: 5
|
27
|
-
username: translatable
|
27
|
+
username: translatable
|
28
28
|
password:
|
29
29
|
host: localhost
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -11,7 +11,18 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
14
|
+
ActiveRecord::Schema.define(:version => 20130124100555) do
|
15
|
+
|
16
|
+
create_table "foos", :force => true do |t|
|
17
|
+
t.datetime "created_at", :null => false
|
18
|
+
t.datetime "updated_at", :null => false
|
19
|
+
end
|
20
|
+
|
21
|
+
create_table "noarraythings", :force => true do |t|
|
22
|
+
t.string "locales"
|
23
|
+
t.datetime "created_at", :null => false
|
24
|
+
t.datetime "updated_at", :null => false
|
25
|
+
end
|
15
26
|
|
16
27
|
create_table "somethings", :force => true do |t|
|
17
28
|
t.string_array "locales", :limit => 255
|