activerecord_translatable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
- include ActiveRecordTranslatable
11
- translate :title
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
- def change
33
- create_table :my_model do |t|
34
- t.string_array :locales
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
- VERSION = "0.0.1"
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 available_locales
15
- self.locales.map { |locale| locale.to_sym }
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
- @translations[locale][attribute] ||= I18n.t("#{self.base_name}.#{attribute}-#{self.id}",
22
- locale: locale, raise: true)
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
- @translations[locale][attribute]
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
- @translations[locale][attribute] = value
48
+ translations[locale][attribute] = value
31
49
  end
32
-
50
+
33
51
  def write_translations
34
- @translations.each do |locale, translations|
35
- translations.each do |attribute, value|
36
- I18n.backend.store_translations(locale, { "#{self.base_name}.#{attribute}-#{self.id}" => value }, escape: false)
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
- self.translateable.each do |attribute|
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
- self.translateable.each do |attribute|
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 :translateable, :base_name
95
+ cattr_accessor :_translatable
76
96
  end
77
97
 
78
98
  module ClassMethods
79
99
  def translate(*attributes)
80
- self.base_name = self.name.downcase
81
- self.translateable ||= []
82
- self.translateable = self.translateable.concat(attributes)
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
@@ -0,0 +1,5 @@
1
+ module ActiveRecordTranslatable
2
+
3
+ VERSION = "0.0.2"
4
+
5
+ end
@@ -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.translateable.should include(:name)
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
@@ -0,0 +1,2 @@
1
+ class Foo < ActiveRecord::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ class Noarraything < ActiveRecord::Base
2
+ serialize :locales
3
+ translate :name
4
+ end
@@ -1,5 +1,3 @@
1
1
  class Something < ActiveRecord::Base
2
- include ActiveRecordTranslatable
3
-
4
2
  translate :name
5
3
  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: translatable
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
@@ -0,0 +1,7 @@
1
+ class CreateFoo < ActiveRecord::Migration
2
+ def change
3
+ create_table :foos do |t|
4
+ t.timestamps
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ class CreateNoarraythings < ActiveRecord::Migration
2
+ def change
3
+ create_table :noarraythings do |t|
4
+ t.string :locales
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -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 => 20121015083259) do
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