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