multilang-hstore 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.
- data/README.rdoc +134 -0
- data/lib/multilang-hstore/active_record_extensions.rb +114 -0
- data/lib/multilang-hstore/translation_keeper.rb +103 -0
- data/lib/multilang-hstore/translation_proxy.rb +22 -0
- data/lib/multilang-hstore/version.rb +3 -0
- data/lib/multilang-hstore.rb +7 -0
- data/spec/multilang_spec.rb +258 -0
- data/spec/schema.rb +19 -0
- data/spec/spec_helper.rb +63 -0
- metadata +59 -0
data/README.rdoc
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
== Multilang-hstore
|
2
|
+
|
3
|
+
Multilang is a small translation library for translating database values for Rails 3 using the [Hstore datatype](http://www.postgresql.org/docs/9.0/static/hstore.html).
|
4
|
+
|
5
|
+
This project is a fork of [artworklv/multilang](https://github.com/artworklv/multilang) with some remarkable differences:
|
6
|
+
|
7
|
+
* Replaced YAML text fields in favor of Hstore fields.
|
8
|
+
* The translation hash is no longer limited to locales in `I18n.available_locales`.
|
9
|
+
|
10
|
+
It uses [engageis/activerecord-postgres-hstore](https://github.com/engageis/activerecord-postgres-hstore)
|
11
|
+
|
12
|
+
== Installation
|
13
|
+
|
14
|
+
You need configure the multilang gem inside your gemfile:
|
15
|
+
|
16
|
+
gem 'multilang-hstore'
|
17
|
+
|
18
|
+
Do not forget to run
|
19
|
+
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
|
23
|
+
== Basic Usage
|
24
|
+
|
25
|
+
This is a walkthrough with all steps you need to setup multilang translated attributes, including model and migration.
|
26
|
+
|
27
|
+
We're assuming here you want a Post model with some multilang attributes, as outlined below:
|
28
|
+
|
29
|
+
class Post < ActiveRecord::Base
|
30
|
+
multilang :title, :accessible => true
|
31
|
+
end
|
32
|
+
|
33
|
+
or
|
34
|
+
|
35
|
+
class Post < ActiveRecord::Base
|
36
|
+
multilang :title, :description, :required => true, :length => 100, :accessible => true
|
37
|
+
end
|
38
|
+
|
39
|
+
The multilang translations are stored in the same model attributes (eg. title):
|
40
|
+
|
41
|
+
You may need to create migration for Post as usual, but multilang attributes should be in hstore type:
|
42
|
+
|
43
|
+
create_table(:posts) do |t|
|
44
|
+
t.hstore :title
|
45
|
+
t.timestamps
|
46
|
+
end
|
47
|
+
|
48
|
+
Thats it!
|
49
|
+
|
50
|
+
Now you are able to translate values for the attributes :title and :description per locale:
|
51
|
+
|
52
|
+
I18n.locale = :en
|
53
|
+
post.title = 'Multilang rocks!'
|
54
|
+
I18n.locale = :lv
|
55
|
+
post.title = 'Multilang rulle!'
|
56
|
+
|
57
|
+
I18n.locale = :en
|
58
|
+
post.title #=> Multilang rocks!
|
59
|
+
I18n.locale = :lv
|
60
|
+
post.title #=> Multilang rulle!
|
61
|
+
|
62
|
+
|
63
|
+
You may assign attributes through auto generated methods (this methods depend from I18n.available_locales):
|
64
|
+
|
65
|
+
I18n.available_locales #=> [:en. :lv]
|
66
|
+
|
67
|
+
post.title_en = 'Multilang rocks!'
|
68
|
+
post.title_lv = 'Multilang rulle!'
|
69
|
+
|
70
|
+
post.title_en #=> 'Multilang rocks!'
|
71
|
+
post.title_lv #=> 'Multilang rulle!'
|
72
|
+
|
73
|
+
You may use mass assignment on model creation (if :accessible param is defined):
|
74
|
+
|
75
|
+
Post.new(:title => {:en => 'Multilang rocks!', :lv => 'Multilang rulle!'})
|
76
|
+
|
77
|
+
or
|
78
|
+
|
79
|
+
Post.new(:title_en => 'Multilang rocks!', :title_lv => 'Multilang rulle!')
|
80
|
+
|
81
|
+
Also, you may ise same hashes with setters:
|
82
|
+
|
83
|
+
post.title = {:en => 'Multilang rocks!', :lv => 'Multilang rulle!'}
|
84
|
+
|
85
|
+
== Attribute methods
|
86
|
+
|
87
|
+
You may get other translations via attribute translation method:
|
88
|
+
|
89
|
+
post.title.translation[:lv] #=> 'Multilang rocks!'
|
90
|
+
post.title.translation[:en] #=> 'Multilang rulle!'
|
91
|
+
post.title.translation.locales #=> [:en, :lv]
|
92
|
+
|
93
|
+
If you have incomplete translations, you can get translation from other locale:
|
94
|
+
|
95
|
+
post.title = {:en => 'Multilang rocks!', :lv => ''}
|
96
|
+
I18n.locale = :lv
|
97
|
+
post.title.any #=> 'Multilang rocks!'
|
98
|
+
|
99
|
+
The value from "any" method returns value for I18n.current_locale or, if value is empty, it searches through all locales. It takes searching order from I18n.available_locales array.
|
100
|
+
|
101
|
+
== Validations
|
102
|
+
Multilang has some validation features:
|
103
|
+
|
104
|
+
multilang :title, :length => 100 #define maximal length validator
|
105
|
+
multilang :title, :required => true #define requirement validator
|
106
|
+
multilang :title, :format => /regexp/ #define validates_format_of validator
|
107
|
+
|
108
|
+
== Tests
|
109
|
+
|
110
|
+
Test runs using a temporary database in your local PostgreSQL server:
|
111
|
+
|
112
|
+
Create a test database:
|
113
|
+
|
114
|
+
$ createdb multilang-hstore-test
|
115
|
+
|
116
|
+
Create the [hstore extension](http://www.postgresql.org/docs/9.1/static/sql-createextension.html):
|
117
|
+
psql -d multilang-hstore-test -c "CREATE EXTENSION IF NOT EXISTS hstore"
|
118
|
+
|
119
|
+
Create the role *postgres* if necessary:
|
120
|
+
|
121
|
+
$ createuser -s -r postgres
|
122
|
+
|
123
|
+
Finally, you can run your tests:
|
124
|
+
|
125
|
+
rspec
|
126
|
+
|
127
|
+
== Bugs and Feedback
|
128
|
+
|
129
|
+
Use [http://github.com/artworklv/multilang/issues](http://github.com/artworklv/multilang/issues)
|
130
|
+
|
131
|
+
== License(MIT)
|
132
|
+
|
133
|
+
Copyright (c) 2010 Arthur Meinart
|
134
|
+
Copyright (c) 2012 Firebase.co
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Multilang
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
|
6
|
+
def multilang_accessible_translations
|
7
|
+
class_variable_get(:@@multilang_accessible_translations)
|
8
|
+
end
|
9
|
+
|
10
|
+
def multilang(*args)
|
11
|
+
|
12
|
+
options = {
|
13
|
+
:required => false,
|
14
|
+
:length => nil,
|
15
|
+
:accessible => false,
|
16
|
+
:format => nil
|
17
|
+
}.merge(args.extract_options!)
|
18
|
+
|
19
|
+
options.assert_valid_keys([:required, :length, :accessible, :format, :nil])
|
20
|
+
|
21
|
+
define_translation_base! unless included_modules.include?(InstanceMethods)
|
22
|
+
|
23
|
+
args.each do |attribute|
|
24
|
+
|
25
|
+
define_method attribute do
|
26
|
+
multilang_translation_keeper(attribute).value
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method "#{attribute}=" do |value|
|
30
|
+
multilang_translation_keeper(attribute).update(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
#attribute accessibility for mass assignment
|
34
|
+
if options[:accessible]
|
35
|
+
matr = multilang_accessible_translations + [attribute.to_sym]
|
36
|
+
class_variable_set(:@@multilang_accessible_translations, matr)
|
37
|
+
end
|
38
|
+
|
39
|
+
module_eval do
|
40
|
+
serialize "#{attribute}", ActiveRecord::Coders::Hstore
|
41
|
+
end
|
42
|
+
|
43
|
+
I18n.available_locales.each do |locale|
|
44
|
+
|
45
|
+
define_method "#{attribute}_#{locale}" do
|
46
|
+
multilang_translation_keeper(attribute)[locale]
|
47
|
+
end
|
48
|
+
|
49
|
+
define_method "#{attribute}_#{locale}=" do |value|
|
50
|
+
multilang_translation_keeper(attribute)[locale] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
# locale based attribute accessibility for mass assignment
|
54
|
+
if options[:accessible]
|
55
|
+
matr = multilang_accessible_translations + ["#{attribute}_#{locale}".to_sym]
|
56
|
+
class_variable_set(:@@multilang_accessible_translations, matr)
|
57
|
+
end
|
58
|
+
|
59
|
+
# attribute presence validator
|
60
|
+
if options[:required]
|
61
|
+
module_eval do
|
62
|
+
validates_presence_of "#{attribute}_#{locale}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
#attribute maximal length validator
|
67
|
+
if options[:length]
|
68
|
+
module_eval do
|
69
|
+
validates_length_of "#{attribute}_#{locale}", :maximum => options[:length]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
#attribute format validator
|
74
|
+
if options[:format]
|
75
|
+
module_eval do
|
76
|
+
validates_format_of "#{attribute}_#{locale}", :with => options[:format]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def define_translation_base!
|
87
|
+
include InstanceMethods
|
88
|
+
|
89
|
+
define_method "multilang_translation_keeper" do |attribute|
|
90
|
+
unless instance_variable_defined?("@multilang_attribute_#{attribute}")
|
91
|
+
instance_variable_set("@multilang_attribute_#{attribute}", MultilangTranslationKeeper.new(self, attribute))
|
92
|
+
end
|
93
|
+
instance_variable_get "@multilang_attribute_#{attribute}"
|
94
|
+
end
|
95
|
+
|
96
|
+
unless class_variable_defined?(:@@multilang_accessible_translations)
|
97
|
+
class_variable_set(:@@multilang_accessible_translations, [])
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
module InstanceMethods
|
105
|
+
def mass_assignment_authorizer(role = :default)
|
106
|
+
super(role) + (self.class.multilang_accessible_translations || [])
|
107
|
+
end
|
108
|
+
end #module InstanceMethods
|
109
|
+
|
110
|
+
end #module ActiveRecordExtensions
|
111
|
+
end ##module Multilang
|
112
|
+
|
113
|
+
ActiveRecord::Base.extend Multilang::ActiveRecordExtensions::ClassMethods
|
114
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Multilang
|
2
|
+
|
3
|
+
class MultilangTranslationKeeper
|
4
|
+
attr_reader :model
|
5
|
+
attr_reader :attribute
|
6
|
+
attr_reader :translations
|
7
|
+
|
8
|
+
def initialize(model, attribute)
|
9
|
+
@model = model
|
10
|
+
@attribute = attribute
|
11
|
+
@translations = {}
|
12
|
+
load!
|
13
|
+
end
|
14
|
+
|
15
|
+
def value(locale = nil)
|
16
|
+
locale ||= actual_locale
|
17
|
+
read(locale)
|
18
|
+
end
|
19
|
+
|
20
|
+
def current_or_any_value
|
21
|
+
v = value
|
22
|
+
if v.empty?
|
23
|
+
reduced_locales = locales - [actual_locale]
|
24
|
+
reduced_locales.each do |locale|
|
25
|
+
v = value(locale)
|
26
|
+
return v unless v.empty?
|
27
|
+
end
|
28
|
+
else
|
29
|
+
return v
|
30
|
+
end
|
31
|
+
return ''
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
raw_read(actual_locale)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_str(locale = nil)
|
39
|
+
locale ||= actual_locale
|
40
|
+
raw_read(locale)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update(value)
|
44
|
+
if value.is_a?(Hash)
|
45
|
+
clear
|
46
|
+
value.each{|k, v| write(k, v)}
|
47
|
+
elsif value.is_a?(String)
|
48
|
+
write(actual_locale, value)
|
49
|
+
end
|
50
|
+
flush!
|
51
|
+
end
|
52
|
+
|
53
|
+
def [](locale)
|
54
|
+
read(locale)
|
55
|
+
end
|
56
|
+
|
57
|
+
def []=(locale, value)
|
58
|
+
write(locale, value)
|
59
|
+
flush!
|
60
|
+
end
|
61
|
+
|
62
|
+
def locales
|
63
|
+
@translations.keys.map(&:to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def empty?
|
67
|
+
@translations.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def actual_locale
|
73
|
+
I18n.locale
|
74
|
+
end
|
75
|
+
|
76
|
+
def write(locale, value)
|
77
|
+
@translations[locale.to_s] = value
|
78
|
+
end
|
79
|
+
|
80
|
+
def read(locale)
|
81
|
+
MultilangTranslationProxy.new(self, locale)
|
82
|
+
end
|
83
|
+
|
84
|
+
def raw_read(locale)
|
85
|
+
@translations[locale.to_s] || ''
|
86
|
+
end
|
87
|
+
|
88
|
+
def clear
|
89
|
+
@translations.clear
|
90
|
+
end
|
91
|
+
|
92
|
+
def load!
|
93
|
+
data = @model[@attribute]
|
94
|
+
data = data.blank? ? nil : data
|
95
|
+
@translations = data.is_a?(Hash) ? data : {}
|
96
|
+
end
|
97
|
+
|
98
|
+
def flush!
|
99
|
+
@model[@attribute] = @translations
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Multilang
|
2
|
+
class MultilangTranslationProxy < String
|
3
|
+
attr_reader :multilang_keeper
|
4
|
+
|
5
|
+
def initialize(multi_keeper, locale)
|
6
|
+
@multilang_keeper = multi_keeper
|
7
|
+
super(@multilang_keeper.to_str(locale))
|
8
|
+
end
|
9
|
+
|
10
|
+
def translation
|
11
|
+
@multilang_keeper
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
String.new(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def any
|
19
|
+
@multilang_keeper.current_or_any_value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
$KCODE = "U"
|
3
|
+
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
5
|
+
|
6
|
+
describe Multilang do
|
7
|
+
before :each do
|
8
|
+
setup_db
|
9
|
+
I18n.locale = :lv
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
teardown_db
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should test database connection" do
|
17
|
+
AbstractPost.create
|
18
|
+
AbstractPost.count.should == 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should add getters/setters in RegularPost" do
|
22
|
+
regular_post = RegularPost.new
|
23
|
+
%w(title body).each do |attr|
|
24
|
+
regular_post.should respond_to(attr)
|
25
|
+
regular_post.should respond_to("#{attr}=")
|
26
|
+
I18n.available_locales.each do |loc|
|
27
|
+
regular_post.should respond_to("#{attr}_#{loc}")
|
28
|
+
regular_post.should respond_to("#{attr}_#{loc}=")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should set attributes in RegularPost" do
|
34
|
+
rp = RegularPost.new
|
35
|
+
|
36
|
+
# set
|
37
|
+
rp.title = "Latviešu nosaukums"
|
38
|
+
rp.body = "Latviešu apraksts"
|
39
|
+
I18n.locale = :ru
|
40
|
+
rp.title = "Русский заголовок"
|
41
|
+
rp.body = "Русский текст"
|
42
|
+
|
43
|
+
# test
|
44
|
+
I18n.locale = :lv
|
45
|
+
rp.title.should == "Latviešu nosaukums"
|
46
|
+
rp.body.should == "Latviešu apraksts"
|
47
|
+
|
48
|
+
I18n.locale = :ru
|
49
|
+
rp.title.should == "Русский заголовок"
|
50
|
+
rp.body.should == "Русский текст"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should set attributes through alternative setters in RegularPost" do
|
54
|
+
rp = RegularPost.new
|
55
|
+
|
56
|
+
# set
|
57
|
+
rp.title_lv = "Latviešu nosaukums"
|
58
|
+
rp.body_lv = "Latviešu apraksts"
|
59
|
+
rp.title_ru = "Русский заголовок"
|
60
|
+
rp.body_ru = "Русский текст"
|
61
|
+
|
62
|
+
# test
|
63
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
64
|
+
rp.body_lv.should == "Latviešu apraksts"
|
65
|
+
rp.title_ru.should == "Русский заголовок"
|
66
|
+
rp.body_ru.should == "Русский текст"
|
67
|
+
|
68
|
+
I18n.locale = :lv
|
69
|
+
rp.title.should == "Latviešu nosaukums"
|
70
|
+
rp.body.should == "Latviešu apraksts"
|
71
|
+
|
72
|
+
I18n.locale = :ru
|
73
|
+
rp.title.should == "Русский заголовок"
|
74
|
+
rp.body.should == "Русский текст"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should set attributes through fullset hash in RegularPost" do
|
78
|
+
rp = RegularPost.new
|
79
|
+
|
80
|
+
# set
|
81
|
+
rp.title = {:lv => "Latviešu nosaukums", :ru => "Русский заголовок"}
|
82
|
+
rp.body = {:lv => "Latviešu apraksts", :ru => "Русский текст"}
|
83
|
+
|
84
|
+
# test
|
85
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
86
|
+
rp.body_lv.should == "Latviešu apraksts"
|
87
|
+
rp.title_ru.should == "Русский заголовок"
|
88
|
+
rp.body_ru.should == "Русский текст"
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should set attributes through halfset hash in RegularPost" do
|
92
|
+
rp = RegularPost.new
|
93
|
+
|
94
|
+
# set
|
95
|
+
rp.title = {:lv => "Latviešu nosaukums"}
|
96
|
+
rp.body = {:lv => "Latviešu apraksts"}
|
97
|
+
|
98
|
+
# test
|
99
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
100
|
+
rp.body_lv.should == "Latviešu apraksts"
|
101
|
+
rp.title_ru.should == ""
|
102
|
+
rp.body_ru.should == ""
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should be validated in RegularPost" do
|
106
|
+
rp = RegularPost.new
|
107
|
+
|
108
|
+
rp.title = "Latviešu nosaukums"
|
109
|
+
rp.body = "Latviešu apraksts"
|
110
|
+
I18n.locale = :ru
|
111
|
+
rp.title = "Русский заголовок"
|
112
|
+
rp.body = "Русский текст"
|
113
|
+
|
114
|
+
rp.should be_valid
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should not be validated in RegularPost" do
|
118
|
+
rp = RegularPost.new
|
119
|
+
|
120
|
+
rp.title_lv = "Latviešu nosaukums"*100 #too long
|
121
|
+
rp.body_lv = "" #empty
|
122
|
+
rp.title_ru = "" #empty
|
123
|
+
rp.body_ru = "Русский текст"*1000 #too long
|
124
|
+
|
125
|
+
rp.valid?
|
126
|
+
|
127
|
+
rp.should have(4).errors
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should mass assign attributes in RegularPost" do
|
131
|
+
rp = RegularPost.new( :title_lv => "Latviešu nosaukums",
|
132
|
+
:title_ru => "Русский заголовок",
|
133
|
+
:body_lv => "Latviešu apraksts",
|
134
|
+
:body_ru => "Русский текст" )
|
135
|
+
|
136
|
+
# test
|
137
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
138
|
+
rp.body_lv.should == "Latviešu apraksts"
|
139
|
+
rp.title_ru.should == "Русский заголовок"
|
140
|
+
rp.body_ru.should == "Русский текст"
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should not mass assign attributes in ProtectedPost" do
|
144
|
+
rp = ProtectedPost.new( :title_lv => "Latviešu nosaukums",
|
145
|
+
:title_ru => "Русский заголовок",
|
146
|
+
:body_lv => "Latviešu apraksts",
|
147
|
+
:body_ru => "Русский текст" )
|
148
|
+
|
149
|
+
# test
|
150
|
+
rp.title_lv.should_not == "Latviešu nosaukums"
|
151
|
+
rp.body_lv.should_not == "Latviešu apraksts"
|
152
|
+
rp.title_ru.should_not == "Русский заголовок"
|
153
|
+
rp.body_ru.should_not == "Русский текст"
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
it "should mass assign attributes in RegulularPost, v2" do
|
158
|
+
rp = RegularPost.new( { :title => {
|
159
|
+
:lv => "Latviešu nosaukums",
|
160
|
+
:ru => "Русский заголовок"
|
161
|
+
},
|
162
|
+
:body => {:lv => "Latviešu apraksts",
|
163
|
+
:ru => "Русский текст"
|
164
|
+
}
|
165
|
+
} )
|
166
|
+
|
167
|
+
# test
|
168
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
169
|
+
rp.body_lv.should == "Latviešu apraksts"
|
170
|
+
rp.title_ru.should == "Русский заголовок"
|
171
|
+
rp.body_ru.should == "Русский текст"
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should not not mass assign attributes in ProtectedPost, v2" do
|
175
|
+
rp = ProtectedPost.new( { :title => {
|
176
|
+
:lv => "Latviešu nosaukums",
|
177
|
+
:ru => "Русский заголовок"
|
178
|
+
},
|
179
|
+
:body => {:lv => "Latviešu apraksts",
|
180
|
+
:ru => "Русский текст"
|
181
|
+
}
|
182
|
+
} )
|
183
|
+
|
184
|
+
# test
|
185
|
+
rp.title_lv.should_not == "Latviešu nosaukums"
|
186
|
+
rp.body_lv.should_not == "Latviešu apraksts"
|
187
|
+
rp.title_ru.should_not == "Русский заголовок"
|
188
|
+
rp.body_ru.should_not == "Русский текст"
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should save/load attributes in RegularPost" do
|
192
|
+
rp = RegularPost.new( :title_lv => "Latviešu nosaukums",
|
193
|
+
:title_ru => "Русский заголовок",
|
194
|
+
:body_lv => "Latviešu apraksts",
|
195
|
+
:body_ru => "Русский текст" )
|
196
|
+
|
197
|
+
rp.save
|
198
|
+
|
199
|
+
rp = RegularPost.last
|
200
|
+
|
201
|
+
rp.title_lv.should == "Latviešu nosaukums"
|
202
|
+
rp.body_lv.should == "Latviešu apraksts"
|
203
|
+
rp.title_ru.should == "Русский заголовок"
|
204
|
+
rp.body_ru.should == "Русский текст"
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should save attributes in db as indexable values" do
|
208
|
+
rp = RegularPost.new( :title_lv => "Latviešu nosaukums",
|
209
|
+
:title_ru => "Русский заголовок",
|
210
|
+
:body_lv => "Latviešu apraksts",
|
211
|
+
:body_ru => "Русский текст" )
|
212
|
+
|
213
|
+
rp.save
|
214
|
+
|
215
|
+
rp = RegularPost.last
|
216
|
+
|
217
|
+
rp.title_before_type_cast.should == {"lv"=>"Latviešu nosaukums", "ru"=>"Русский заголовок"}
|
218
|
+
rp.body_before_type_cast.should == {"lv"=>"Latviešu apraksts", "ru"=>"Русский текст"}
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should return proxied attribute" do
|
222
|
+
rp = RegularPost.new( :title_lv => "Latviešu nosaukums",
|
223
|
+
:title_ru => "Русский заголовок",
|
224
|
+
:body_lv => "Latviešu apraksts",
|
225
|
+
:body_ru => "Русский текст" )
|
226
|
+
|
227
|
+
|
228
|
+
rp.title.should be_an_instance_of(Multilang::MultilangTranslationProxy)
|
229
|
+
rp.title.should be_a_kind_of(String)
|
230
|
+
|
231
|
+
rp.body.should respond_to(:translation)
|
232
|
+
rp.title.translation[:lv].should == "Latviešu nosaukums"
|
233
|
+
rp.body.translation[:ru].should == "Русский текст"
|
234
|
+
|
235
|
+
rp.title.to_s.should be_an_instance_of(String)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should set/get some attributes in/from PartialrPost" do
|
239
|
+
rp = RegularPost.new
|
240
|
+
|
241
|
+
# set
|
242
|
+
rp.title = "Latviešu nosaukums"
|
243
|
+
rp.body = "Latviešu apraksts"
|
244
|
+
|
245
|
+
# test
|
246
|
+
I18n.locale = :lv
|
247
|
+
rp.title.should == "Latviešu nosaukums"
|
248
|
+
rp.body.should == "Latviešu apraksts"
|
249
|
+
|
250
|
+
I18n.locale = :ru
|
251
|
+
rp.title.should == ""
|
252
|
+
rp.body.should == ""
|
253
|
+
|
254
|
+
rp.title.any.should == "Latviešu nosaukums"
|
255
|
+
rp.body.any.should == "Latviešu apraksts"
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
create_table :abstract_posts do |t|
|
3
|
+
t.hstore :title
|
4
|
+
t.hstore :body
|
5
|
+
t.integer :void, :default => 1
|
6
|
+
end
|
7
|
+
|
8
|
+
#create_table :posts do |t|
|
9
|
+
# t.text :title
|
10
|
+
# t.text :body
|
11
|
+
# t.integer :void, :default => 1
|
12
|
+
#end
|
13
|
+
|
14
|
+
#create_table :pages do |t|
|
15
|
+
# t.text :title
|
16
|
+
# t.text :body
|
17
|
+
# t.integer :void, :default => 1
|
18
|
+
#end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_record'
|
7
|
+
require 'activerecord-postgres-hstore/activerecord'
|
8
|
+
require 'activerecord-postgres-hstore/coder'
|
9
|
+
require 'activerecord-postgres-hstore/hash'
|
10
|
+
require 'activerecord-postgres-hstore/string'
|
11
|
+
require 'multilang-hstore'
|
12
|
+
require 'logger'
|
13
|
+
|
14
|
+
ActiveRecord::Base.logger = Logger.new(nil)
|
15
|
+
ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "multilang-hstore-test", :host=>'127.0.0.1', :user=>'postgres')
|
16
|
+
|
17
|
+
I18n.available_locales = [:lv, :ru]
|
18
|
+
I18n.locale = I18n.default_locale = :lv
|
19
|
+
|
20
|
+
def setup_db
|
21
|
+
ActiveRecord::Migration.verbose = false
|
22
|
+
load "schema.rb"
|
23
|
+
end
|
24
|
+
|
25
|
+
def teardown_db
|
26
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
27
|
+
ActiveRecord::Base.connection.drop_table(table)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#testable models
|
32
|
+
class AbstractPost < ActiveRecord::Base
|
33
|
+
serialize :title, ActiveRecord::Coders::Hstore
|
34
|
+
serialize :body, ActiveRecord::Coders::Hstore
|
35
|
+
end
|
36
|
+
|
37
|
+
class MinimalPost < ActiveRecord::Base
|
38
|
+
self.table_name = 'abstract_posts'
|
39
|
+
multilang :title, :required => false, :accessible => true
|
40
|
+
multilang :body, :required => false, :accessible => true
|
41
|
+
attr_accessible :void
|
42
|
+
end
|
43
|
+
|
44
|
+
class RegularPost < ActiveRecord::Base
|
45
|
+
self.table_name = 'abstract_posts'
|
46
|
+
multilang :title, :required => true, :length => 25, :accessible => true
|
47
|
+
multilang :body, :required => true, :length => 1000, :accessible => true
|
48
|
+
attr_accessible :void
|
49
|
+
end
|
50
|
+
|
51
|
+
class ProtectedPost < ActiveRecord::Base
|
52
|
+
self.table_name = 'abstract_posts'
|
53
|
+
multilang :title, :accessible => false
|
54
|
+
multilang :body, :accessible => false
|
55
|
+
attr_accessible :void
|
56
|
+
end
|
57
|
+
|
58
|
+
class PartialPost < ActiveRecord::Base
|
59
|
+
self.table_name = 'abstract_posts'
|
60
|
+
multilang :title
|
61
|
+
multilang :body
|
62
|
+
attr_accessible :void
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: multilang-hstore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Arthur Meinart
|
9
|
+
- Firebase.co
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-10-29 00:00:00.000000000 Z
|
14
|
+
dependencies: []
|
15
|
+
description: Model translations for Rails 3 backed by PostgreSQL and Hstore
|
16
|
+
email: hello@firebase.co
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files:
|
20
|
+
- README.rdoc
|
21
|
+
files:
|
22
|
+
- lib/multilang-hstore.rb
|
23
|
+
- lib/multilang-hstore/active_record_extensions.rb
|
24
|
+
- lib/multilang-hstore/translation_keeper.rb
|
25
|
+
- lib/multilang-hstore/translation_proxy.rb
|
26
|
+
- lib/multilang-hstore/version.rb
|
27
|
+
- README.rdoc
|
28
|
+
- spec/multilang_spec.rb
|
29
|
+
- spec/schema.rb
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
homepage: http://github.com/firebaseco/multilang-hstore
|
32
|
+
licenses: []
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.24
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: Model translations for Rails 3 backed by PostgreSQL and Hstore
|
55
|
+
test_files:
|
56
|
+
- spec/multilang_spec.rb
|
57
|
+
- spec/schema.rb
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
has_rdoc:
|