single_table_globalize3 0.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +61 -0
- data/LICENSE +21 -0
- data/Rakefile +22 -0
- data/lib/generators/globalize3/migration_generator.rb +14 -0
- data/lib/generators/globalize3/templates/migration.rb +22 -0
- data/lib/globalize/active_record/act_macro.rb +66 -0
- data/lib/globalize/active_record/adapter.rb +112 -0
- data/lib/globalize/active_record/attributes.rb +27 -0
- data/lib/globalize/active_record/class_methods.rb +168 -0
- data/lib/globalize/active_record/exceptions.rb +19 -0
- data/lib/globalize/active_record/instance_methods.rb +210 -0
- data/lib/globalize/active_record/translation.rb +43 -0
- data/lib/globalize/active_record.rb +12 -0
- data/lib/globalize.rb +74 -0
- data/lib/i18n/missing_translations_log_handler.rb +41 -0
- data/lib/i18n/missing_translations_raise_handler.rb +25 -0
- data/lib/patches/active_record/query_method.rb +35 -0
- data/lib/patches/active_record/uniqueness_validator.rb +61 -0
- data/lib/patches/active_record/xml_attribute_serializer.rb +13 -0
- data/lib/single_table_globalize3/version.rb +3 -0
- data/lib/single_table_globalize3.rb +1 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4d9d9b6bb1b7c9a01b80fa37c207d172b26bf321
|
4
|
+
data.tar.gz: 1f6344d88c60c9a226491d3280b2e23ae639e621
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3c82d94486a79cd745cdba46ca13463ad2cda67b321b654263c9e598a4bdb3422a9e286125c61407827c2d55068d1558b0b0c5d421a3b9302ce7ffdce0f2f45e
|
7
|
+
data.tar.gz: b22a0c72c1cad41885978ebe89b389afbd727e3bdeb230467beea39f2a3e54c694c76a6be52f30583a8cc01fe820fa077b7ab71b5456a1152a3200ff4050d74d
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
single_table_globalize3 (0.3.0)
|
5
|
+
activemodel (>= 3.0.0)
|
6
|
+
activerecord (>= 3.0.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activemodel (3.2.12)
|
12
|
+
activesupport (= 3.2.12)
|
13
|
+
builder (~> 3.0.0)
|
14
|
+
activerecord (3.2.12)
|
15
|
+
activemodel (= 3.2.12)
|
16
|
+
activesupport (= 3.2.12)
|
17
|
+
arel (~> 3.0.2)
|
18
|
+
tzinfo (~> 0.3.29)
|
19
|
+
activesupport (3.2.12)
|
20
|
+
i18n (~> 0.6)
|
21
|
+
multi_json (~> 1.0)
|
22
|
+
arel (3.0.2)
|
23
|
+
builder (3.0.4)
|
24
|
+
coderay (1.0.8)
|
25
|
+
database_cleaner (0.6.7)
|
26
|
+
i18n (0.6.1)
|
27
|
+
json (1.7.7)
|
28
|
+
metaclass (0.0.1)
|
29
|
+
method_source (0.8.1)
|
30
|
+
mocha (0.13.2)
|
31
|
+
metaclass (~> 0.0.1)
|
32
|
+
multi_json (1.6.1)
|
33
|
+
pathname_local (0.0.2)
|
34
|
+
pry (0.9.12)
|
35
|
+
coderay (~> 1.0.5)
|
36
|
+
method_source (~> 0.8)
|
37
|
+
slop (~> 3.4)
|
38
|
+
pry-nav (0.2.3)
|
39
|
+
pry (~> 0.9.10)
|
40
|
+
rake (10.0.3)
|
41
|
+
rdoc (3.12.1)
|
42
|
+
json (~> 1.4)
|
43
|
+
slop (3.4.3)
|
44
|
+
sqlite3 (1.3.7)
|
45
|
+
test_declarative (0.0.5)
|
46
|
+
tzinfo (0.3.35)
|
47
|
+
|
48
|
+
PLATFORMS
|
49
|
+
ruby
|
50
|
+
|
51
|
+
DEPENDENCIES
|
52
|
+
database_cleaner (~> 0.6.0)
|
53
|
+
mocha
|
54
|
+
pathname_local
|
55
|
+
pry
|
56
|
+
pry-nav
|
57
|
+
rake
|
58
|
+
rdoc
|
59
|
+
single_table_globalize3!
|
60
|
+
sqlite3
|
61
|
+
test_declarative
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2008, 2009, 2010, 2012 Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader, Trong Tran
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Run all tests.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'SingleTableGlobalize3'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails/generators/active_record'
|
2
|
+
|
3
|
+
module SingleTableGlobalize3
|
4
|
+
module Generators
|
5
|
+
class MigrationGenerator < ActiveRecord::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
argument :name, :type => :string, :default => "application"
|
8
|
+
|
9
|
+
def copy_devise_migration
|
10
|
+
migration_template "migration.rb", "db/migrate/create_globalize_translations"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateGlobalizeTranslations < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
create_table :globalize_translations do |t|
|
4
|
+
t.integer :translatable_id
|
5
|
+
t.string :translatable_type
|
6
|
+
t.string :locale
|
7
|
+
t.string :attribute_name
|
8
|
+
t.text :value
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
add_index :globalize_translations, [:translatable_id, :translatable_type], :name => :index_globalize_translalation_1
|
12
|
+
add_index :globalize_translations, [:translatable_id, :translatable_type, :locale], :name => :index_globalize_translalation_2
|
13
|
+
add_index :globalize_translations, [:translatable_id, :translatable_type, :locale, :attribute_name], :name => :index_globalize_translalation_3
|
14
|
+
end
|
15
|
+
|
16
|
+
def down
|
17
|
+
remove_index :globalize_translations, :name => :index_globalize_translalation_3
|
18
|
+
remove_index :globalize_translations, :name => :index_globalize_translalation_2
|
19
|
+
remove_index :globalize_translations, :name => :index_globalize_translalation_1
|
20
|
+
drop_table :globalize_translations
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module ActMacro
|
4
|
+
def translates(*attr_names)
|
5
|
+
|
6
|
+
options = attr_names.extract_options!
|
7
|
+
setup_translates!(options) unless translates?
|
8
|
+
|
9
|
+
attr_names = attr_names.map(&:to_sym)
|
10
|
+
attr_names -= translated_attribute_names if defined?(translated_attribute_names)
|
11
|
+
|
12
|
+
if attr_names.present?
|
13
|
+
translation_class.instance_eval %{
|
14
|
+
attr_accessible :#{attr_names.join(', :')}
|
15
|
+
}
|
16
|
+
|
17
|
+
attr_names.each do |attr_name|
|
18
|
+
# Detect and apply serialization.
|
19
|
+
serializer = self.serialized_attributes[attr_name.to_s]
|
20
|
+
if serializer.present?
|
21
|
+
if defined?(::ActiveRecord::Coders::YAMLColumn) &&
|
22
|
+
serializer.is_a?(::ActiveRecord::Coders::YAMLColumn)
|
23
|
+
|
24
|
+
serializer = serializer.object_class
|
25
|
+
end
|
26
|
+
|
27
|
+
translation_class.send :serialize, attr_name, serializer
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create accessors for the attribute.
|
31
|
+
translated_attr_accessor(attr_name)
|
32
|
+
translations_accessor(attr_name)
|
33
|
+
|
34
|
+
# Add attribute to the list.
|
35
|
+
self.translated_attribute_names << attr_name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def translates?
|
41
|
+
included_modules.include?(InstanceMethods)
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
def setup_translates!(options)
|
46
|
+
class_attribute :translated_attribute_names, :translation_options, :fallbacks_for_empty_translations
|
47
|
+
self.translated_attribute_names = []
|
48
|
+
self.translation_options = options
|
49
|
+
self.fallbacks_for_empty_translations = options[:fallbacks_for_empty_translations]
|
50
|
+
|
51
|
+
include InstanceMethods
|
52
|
+
extend ClassMethods
|
53
|
+
|
54
|
+
has_many :translations, :class_name => translation_class.name,
|
55
|
+
:foreign_key => :translatable_id,
|
56
|
+
:dependent => :destroy,
|
57
|
+
:as => :translatable
|
58
|
+
|
59
|
+
after_create :save_translations!
|
60
|
+
after_update :save_translations!
|
61
|
+
|
62
|
+
translation_class.instance_eval %{ attr_accessible :locale }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
class Adapter
|
4
|
+
# The cache caches attributes that already were looked up for read access.
|
5
|
+
# The stash keeps track of new or changed values that need to be saved.
|
6
|
+
attr_accessor :record, :stash, :translations
|
7
|
+
private :record=, :stash=
|
8
|
+
|
9
|
+
delegate :translation_class, :to => :'record.class'
|
10
|
+
|
11
|
+
def initialize(record)
|
12
|
+
self.record = record
|
13
|
+
self.stash = Attributes.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_stash(locale, name)
|
17
|
+
value = stash.read(locale, name)
|
18
|
+
return value if value
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def stash_contains?(locale, name)
|
23
|
+
stash.contains?(locale, name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(locale, name)
|
27
|
+
record.globalize_fallbacks(locale).each do |fallback|
|
28
|
+
value = stash.contains?(fallback, name) ? fetch_stash(fallback, name) : fetch_attribute(fallback, name)
|
29
|
+
|
30
|
+
unless fallbacks_for?(value)
|
31
|
+
set_metadata(value, :locale => fallback, :requested_locale => locale)
|
32
|
+
return value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
return nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def write(locale, name, value)
|
40
|
+
stash.write(locale, name, value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def save_translations!
|
44
|
+
existing_translations = {}
|
45
|
+
record.translations.each do |t|
|
46
|
+
locale_str = t.locale.to_s
|
47
|
+
existing_translations[locale_str] ||= {}
|
48
|
+
existing_translations[locale_str][t.attribute_name] = t
|
49
|
+
end
|
50
|
+
|
51
|
+
stash.each do |locale, attrs|
|
52
|
+
locale_str = locale.to_s
|
53
|
+
attrs.each do |name, value|
|
54
|
+
translation = existing_translations[locale_str].try('[]', name) ||
|
55
|
+
record.translations.build(:locale => locale_str, :attribute_name => name)
|
56
|
+
translation.update_attribute(:value, value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
reset
|
61
|
+
end
|
62
|
+
|
63
|
+
def reset
|
64
|
+
stash.clear
|
65
|
+
end
|
66
|
+
|
67
|
+
protected
|
68
|
+
|
69
|
+
def type_cast(name, value)
|
70
|
+
if value.nil?
|
71
|
+
nil
|
72
|
+
elsif column = column_for_attribute(name)
|
73
|
+
column.type_cast(value)
|
74
|
+
else
|
75
|
+
value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def column_for_attribute(name)
|
80
|
+
translation_class.columns_hash[name.to_s]
|
81
|
+
end
|
82
|
+
|
83
|
+
def unserializable_attribute?(name, column)
|
84
|
+
column.text? && translation_class.serialized_attributes[name.to_s]
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch_attribute(locale, name)
|
88
|
+
translation = record.translation_for(locale, name, false)
|
89
|
+
return translation && translation.value
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_metadata(object, metadata)
|
93
|
+
object.translation_metadata.merge!(metadata) if object.respond_to?(:translation_metadata)
|
94
|
+
object
|
95
|
+
end
|
96
|
+
|
97
|
+
def translation_metadata_accessor(object)
|
98
|
+
return if obj.respond_to?(:translation_metadata)
|
99
|
+
class << object; attr_accessor :translation_metadata end
|
100
|
+
object.translation_metadata ||= {}
|
101
|
+
end
|
102
|
+
|
103
|
+
def fallbacks_for?(object)
|
104
|
+
object.nil? || (fallbacks_for_empty_translations? && object.blank?)
|
105
|
+
end
|
106
|
+
|
107
|
+
def fallbacks_for_empty_translations?
|
108
|
+
record.fallbacks_for_empty_translations
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Helper class for storing values per locale. Used by Globalize::Adapter
|
2
|
+
# to stash and cache attribute values.
|
3
|
+
|
4
|
+
module Globalize
|
5
|
+
module ActiveRecord
|
6
|
+
class Attributes < Hash # TODO: Think about using HashWithIndifferentAccess ?
|
7
|
+
def [](locale)
|
8
|
+
locale = locale.to_sym
|
9
|
+
self[locale] = {} unless has_key?(locale)
|
10
|
+
self.fetch(locale)
|
11
|
+
end
|
12
|
+
|
13
|
+
def contains?(locale, name)
|
14
|
+
self[locale].has_key?(name.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(locale, name)
|
18
|
+
self[locale][name.to_s]
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(locale, name, value)
|
22
|
+
#raise 'z' if value.nil? # TODO
|
23
|
+
self[locale][name.to_s] = value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module ClassMethods
|
4
|
+
delegate :translated_locales, :to => :translation_class
|
5
|
+
|
6
|
+
def with_translations(*locales)
|
7
|
+
locales = [locales].flatten
|
8
|
+
index = locales.pop if locales.last.is_a?(Fixnum)
|
9
|
+
locales = Globalize.fallbacks if locales.empty?
|
10
|
+
joins("LEFT OUTER JOIN #{translation_class.table_name} #{translations_table_name(index)} ON #{translations_table_name(index)}.translatable_id = #{self.table_name}.id").
|
11
|
+
select("distinct #{table_name}.*").
|
12
|
+
where(translated_column_name('locale', index) => locales)
|
13
|
+
end
|
14
|
+
|
15
|
+
def with_translated_attribute(name, value, locales = nil)
|
16
|
+
locales ||= Globalize.fallbacks
|
17
|
+
self.join_index = self.join_index + 1
|
18
|
+
with_translations(locales, self.join_index).where(
|
19
|
+
translated_column_name('attribute_name', self.join_index) => name.to_s,
|
20
|
+
translated_column_name('value', self.join_index) => Array(value).map(&:to_s)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def translated?(name)
|
25
|
+
translated_attribute_names.include?(name.to_sym)
|
26
|
+
end
|
27
|
+
|
28
|
+
def required_attributes
|
29
|
+
validators.map { |v| v.attributes if v.is_a?(ActiveModel::Validations::PresenceValidator) }.flatten
|
30
|
+
end
|
31
|
+
|
32
|
+
def required_translated_attributes
|
33
|
+
translated_attribute_names & required_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
def translation_class
|
37
|
+
Globalize::ActiveRecord::Translation
|
38
|
+
end
|
39
|
+
|
40
|
+
def translations_table_name(index = nil)
|
41
|
+
"#{translation_class.table_name}#{index}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def translated_column_name(name, index = nil)
|
45
|
+
"#{translations_table_name(index)}.#{name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
if RUBY_VERSION < '1.9'
|
49
|
+
def respond_to?(method_id, *args, &block)
|
50
|
+
supported_on_missing?(method_id) || super
|
51
|
+
end
|
52
|
+
else
|
53
|
+
def respond_to_missing?(method_id, include_private = false)
|
54
|
+
supported_on_missing?(method_id) || super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def supported_on_missing?(method_id)
|
59
|
+
return super unless RUBY_VERSION < '1.9' || respond_to?(:translated_attribute_names)
|
60
|
+
match = defined?(::ActiveRecord::DynamicFinderMatch) && (::ActiveRecord::DynamicFinderMatch.match(method_id) || ::ActiveRecord::DynamicScopeMatch.match(method_id))
|
61
|
+
return false if match.nil?
|
62
|
+
|
63
|
+
attribute_names = match.attribute_names.map(&:to_sym)
|
64
|
+
translated_attributes = attribute_names & translated_attribute_names
|
65
|
+
return false if translated_attributes.empty?
|
66
|
+
|
67
|
+
untranslated_attributes = attribute_names - translated_attributes
|
68
|
+
return false if untranslated_attributes.any?{|unt| ! respond_to?(:"scoped_by_#{unt}")}
|
69
|
+
return [match, attribute_names, translated_attributes, untranslated_attributes]
|
70
|
+
end
|
71
|
+
|
72
|
+
def method_missing(method_id, *arguments, &block)
|
73
|
+
match, attribute_names, translated_attributes, untranslated_attributes = supported_on_missing?(method_id)
|
74
|
+
return super unless match
|
75
|
+
|
76
|
+
scope = scoped
|
77
|
+
|
78
|
+
translated_attributes.each do |attr|
|
79
|
+
scope = scope.with_translated_attribute(attr, arguments[attribute_names.index(attr)])
|
80
|
+
end
|
81
|
+
|
82
|
+
untranslated_attributes.each do |unt|
|
83
|
+
index = attribute_names.index(unt)
|
84
|
+
raise StandarError unless index
|
85
|
+
scope = scope.send(:"scoped_by_#{unt}", arguments[index])
|
86
|
+
end
|
87
|
+
|
88
|
+
if defined?(::ActiveRecord::DynamicFinderMatch) && match.is_a?(::ActiveRecord::DynamicFinderMatch)
|
89
|
+
if match.instantiator? and scope.blank?
|
90
|
+
return scope.find_or_instantiator_by_attributes match, attribute_names, *arguments, &block
|
91
|
+
end
|
92
|
+
match_finder_method = match.finder.to_s
|
93
|
+
match_finder_method << "!" if match.bang? && ::ActiveRecord::VERSION::STRING >= "3.1.0"
|
94
|
+
return scope.send(match_finder_method).tap do |found|
|
95
|
+
found.is_a?(Array) ? found.map { |f| f.translations.reload } : found.translations.reload unless found.nil?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
return scope
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_or_instantiator_by_attributes(match, attributes, *args)
|
102
|
+
options = args.size > 1 && args.last(2).all?{ |a| a.is_a?(Hash) } ? args.extract_options! : {}
|
103
|
+
protected_attributes_for_create, unprotected_attributes_for_create = {}, {}
|
104
|
+
args.each_with_index do |arg, i|
|
105
|
+
if arg.is_a?(Hash)
|
106
|
+
protected_attributes_for_create = args[i].with_indifferent_access
|
107
|
+
else
|
108
|
+
unprotected_attributes_for_create[attributes[i]] = args[i]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
record = if ::ActiveRecord::VERSION::STRING < "3.1.0"
|
113
|
+
new do |r|
|
114
|
+
r.send(:attributes=, protected_attributes_for_create, true) unless protected_attributes_for_create.empty?
|
115
|
+
r.send(:attributes=, unprotected_attributes_for_create, false) unless unprotected_attributes_for_create.empty?
|
116
|
+
end
|
117
|
+
else
|
118
|
+
new(protected_attributes_for_create, options) do |r|
|
119
|
+
r.assign_attributes(unprotected_attributes_for_create, :without_protection => true)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
yield(record) if block_given?
|
123
|
+
record.send(match.bang? ? :save! : :save) if match.instantiator.eql?(:create)
|
124
|
+
|
125
|
+
record
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
def translated_attr_accessor(name)
|
131
|
+
define_method(:"#{name}=") do |value|
|
132
|
+
write_attribute(name, value)
|
133
|
+
end
|
134
|
+
define_method(name) do |*args|
|
135
|
+
read_attribute(name, {:locale => args.first})
|
136
|
+
end
|
137
|
+
alias_method :"#{name}_before_type_cast", name
|
138
|
+
end
|
139
|
+
|
140
|
+
def translations_accessor(name)
|
141
|
+
define_method(:"#{name}_translations") do
|
142
|
+
result = translations.each_with_object(HashWithIndifferentAccess.new) do |translation, result|
|
143
|
+
result[translation.locale] = translation.value
|
144
|
+
end
|
145
|
+
globalize.stash.keys.each_with_object(result) do |locale, result|
|
146
|
+
result[locale] = globalize.fetch_stash(locale, name) if globalize.stash_contains?(locale, name)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
define_method(:"#{name}_translations=") do |value|
|
150
|
+
value.each do |(locale, value)|
|
151
|
+
write_attribute name, value, :locale => locale
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def join_index
|
157
|
+
@join_index = 0 if @join_index.nil? || @join_index > 100
|
158
|
+
@join_index
|
159
|
+
end
|
160
|
+
|
161
|
+
def join_index=(value)
|
162
|
+
@join_index = value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module Exceptions
|
4
|
+
class MigrationError < StandardError; end
|
5
|
+
|
6
|
+
class BadFieldName < MigrationError
|
7
|
+
def initialize(field)
|
8
|
+
super("Missing translated field #{field.inspect}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class BadFieldType < MigrationError
|
13
|
+
def initialize(name, type)
|
14
|
+
super("Bad field type for field #{name.inspect} (#{type.inspect}), should be :string or :text")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
module InstanceMethods
|
4
|
+
delegate :translated_locales, :to => :translations
|
5
|
+
|
6
|
+
def globalize
|
7
|
+
@globalize ||= Adapter.new(self)
|
8
|
+
end
|
9
|
+
|
10
|
+
def attributes
|
11
|
+
super.merge(translated_attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
# Maintain Rails 3.0.x compatibility while adding Rails 3.1.x compatibility
|
16
|
+
if base.method_defined?(:assign_attributes)
|
17
|
+
base.class_eval %{
|
18
|
+
def assign_attributes(attributes, options = {})
|
19
|
+
with_given_locale(attributes) { super }
|
20
|
+
end
|
21
|
+
}
|
22
|
+
else
|
23
|
+
base.class_eval %{
|
24
|
+
def attributes=(attributes, *args)
|
25
|
+
with_given_locale(attributes) { super }
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_attributes!(attributes, *args)
|
29
|
+
with_given_locale(attributes) { super }
|
30
|
+
end
|
31
|
+
|
32
|
+
def update_attributes(attributes, *args)
|
33
|
+
with_given_locale(attributes) { super }
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Deprecate old use of locale
|
40
|
+
def deprecated_options(options)
|
41
|
+
unless options.is_a?(Hash)
|
42
|
+
warn "[DEPRECATION] passing 'locale' as #{options.inspect} is deprecated. Please use {:locale => #{options.inspect}} instead."
|
43
|
+
{:locale => options}
|
44
|
+
else
|
45
|
+
options
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def write_attribute(name, value, options = {})
|
50
|
+
if translated?(name)
|
51
|
+
options = {:locale => Globalize.locale}.merge(deprecated_options(options))
|
52
|
+
|
53
|
+
# Dirty tracking, paraphrased from
|
54
|
+
# ActiveRecord::AttributeMethods::Dirty#write_attribute.
|
55
|
+
name_str = name.to_s
|
56
|
+
# If there's already a change, delete it if this undoes the change.
|
57
|
+
if attribute_changed?(name_str) && value == changed_attributes[name_str]
|
58
|
+
changed_attributes.delete(name_str)
|
59
|
+
elsif !attribute_changed?(name_str) && value != (old = globalize.fetch(options[:locale], name))
|
60
|
+
changed_attributes[name_str] = old
|
61
|
+
end
|
62
|
+
|
63
|
+
globalize.write(options[:locale], name, value)
|
64
|
+
else
|
65
|
+
super(name, value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def read_attribute(name, options = {})
|
70
|
+
options = {:translated => true, :locale => nil}.merge(deprecated_options(options))
|
71
|
+
if (self.class.translated?(name) && options[:translated]) && (value = globalize.fetch(options[:locale] || Globalize.locale, name))
|
72
|
+
value
|
73
|
+
else
|
74
|
+
super(name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def attribute_names
|
79
|
+
translated_attribute_names.map(&:to_s) + super
|
80
|
+
end
|
81
|
+
|
82
|
+
def translated?(name)
|
83
|
+
self.class.translated?(name)
|
84
|
+
end
|
85
|
+
|
86
|
+
def translated_attributes
|
87
|
+
translated_attribute_names.inject({}) do |attributes, name|
|
88
|
+
attributes.merge(name.to_s => translation[name.to_s].try(:value))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# This method is basically the method built into Rails
|
93
|
+
# but we have to pass {:translated => false}
|
94
|
+
def untranslated_attributes
|
95
|
+
attrs = {}
|
96
|
+
attribute_names.each do |name|
|
97
|
+
attrs[name] = read_attribute(name, {:translated => false})
|
98
|
+
end
|
99
|
+
attrs
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_translations(options)
|
103
|
+
options.keys.each do |locale|
|
104
|
+
options[locale].each do |key, value|
|
105
|
+
raise NoMethodError.new("unknown attribute: #{key}") unless attribute_names.flatten.include?(key.to_s)
|
106
|
+
translation = translation_for(locale, key.to_s)
|
107
|
+
translation.value = value
|
108
|
+
translation.save
|
109
|
+
end
|
110
|
+
end
|
111
|
+
globalize.reset
|
112
|
+
end
|
113
|
+
|
114
|
+
def reload(options = nil)
|
115
|
+
translation_caches.clear
|
116
|
+
translated_attribute_names.each { |name| @attributes.delete(name.to_s) }
|
117
|
+
globalize.reset
|
118
|
+
super(options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def clone
|
122
|
+
obj = super
|
123
|
+
return obj unless respond_to?(:translated_attribute_names)
|
124
|
+
|
125
|
+
obj.instance_variable_set(:@translations, nil) if new_record? # Reset the collection because of rails bug: http://pastie.org/1521874
|
126
|
+
obj.instance_variable_set(:@globalize, nil )
|
127
|
+
each_locale_and_translated_attribute do |locale, name|
|
128
|
+
obj.globalize.write(locale, name, globalize.fetch(locale, name) )
|
129
|
+
end
|
130
|
+
|
131
|
+
return obj
|
132
|
+
end
|
133
|
+
|
134
|
+
def translation
|
135
|
+
translations_for_locale(::Globalize.locale)
|
136
|
+
end
|
137
|
+
|
138
|
+
def translation_for(locale, name, build_if_missing = true)
|
139
|
+
translation_caches[locale] ||= {}
|
140
|
+
translation_caches[locale][name] ||= (translations.detect{|t| t.locale == locale && t.attribute_name == name.to_s}) ||
|
141
|
+
(translations.attribute(name).with_locale(locale).first) ||
|
142
|
+
(translations.build(:locale => locale, :attribute_name => name) if build_if_missing)
|
143
|
+
end
|
144
|
+
|
145
|
+
def translations_for_locale(locale)
|
146
|
+
translation_caches[locale] = if translations.loaded?
|
147
|
+
translations.select{|t| t.locale.to_s == locale.to_s }
|
148
|
+
else
|
149
|
+
translations.with_locale(locale)
|
150
|
+
end.inject({}) do |hash, t|
|
151
|
+
hash.update(t.attribute_name => t)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def translation_caches
|
156
|
+
@translation_caches ||= {}
|
157
|
+
end
|
158
|
+
|
159
|
+
def globalize_fallbacks(locale)
|
160
|
+
Globalize.fallbacks(locale)
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def update(*)
|
166
|
+
I18n.with_locale(read_attribute(:locale) || I18n.default_locale) do
|
167
|
+
super
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def create(*)
|
172
|
+
I18n.with_locale(read_attribute(:locale) || I18n.default_locale) do
|
173
|
+
super
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
def each_locale_and_translated_attribute
|
180
|
+
used_locales.each do |locale|
|
181
|
+
translated_attribute_names.each do |name|
|
182
|
+
yield locale, name
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def used_locales
|
188
|
+
globalize.stash.keys.concat(globalize.stash.keys).concat(translations.translated_locales).uniq
|
189
|
+
end
|
190
|
+
|
191
|
+
def save_translations!
|
192
|
+
globalize.save_translations!
|
193
|
+
translation_caches.clear
|
194
|
+
end
|
195
|
+
|
196
|
+
def with_given_locale(attributes, &block)
|
197
|
+
attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
|
198
|
+
|
199
|
+
locale = respond_to?(:locale=) ? attributes.try(:[], :locale) :
|
200
|
+
attributes.try(:delete, :locale)
|
201
|
+
|
202
|
+
if locale
|
203
|
+
Globalize.with_locale(locale, &block)
|
204
|
+
else
|
205
|
+
yield
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
class Translation < ::ActiveRecord::Base
|
4
|
+
if self.respond_to?(:table_name=)
|
5
|
+
self.table_name = :globalize_translations
|
6
|
+
else
|
7
|
+
set_table_name :globalize_translations
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessible :locale, :attribute_name, :value
|
11
|
+
validates :locale, :attribute_name, :presence => true
|
12
|
+
belongs_to :translatable, :polymorphic => true
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def with_locales(*locales)
|
16
|
+
# Avoid using "IN" with SQL queries when only using one locale.
|
17
|
+
locales = locales.flatten.map(&:to_s)
|
18
|
+
locales = locales.first if locales.one?
|
19
|
+
where :locale => locales
|
20
|
+
end
|
21
|
+
alias with_locale with_locales
|
22
|
+
|
23
|
+
def translated_locales
|
24
|
+
select('DISTINCT locale').map(&:locale).sort { |l,r| l.to_s <=> r.to_s }
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute(attribute)
|
28
|
+
attribute = attribute.to_s
|
29
|
+
where :attribute_name => attribute
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def locale
|
34
|
+
_locale = read_attribute :locale
|
35
|
+
_locale.present? ? _locale.to_sym : _locale
|
36
|
+
end
|
37
|
+
|
38
|
+
def locale=(locale)
|
39
|
+
write_attribute :locale, locale.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Globalize
|
2
|
+
module ActiveRecord
|
3
|
+
autoload :ActMacro, 'globalize/active_record/act_macro'
|
4
|
+
autoload :Adapter, 'globalize/active_record/adapter'
|
5
|
+
autoload :Attributes, 'globalize/active_record/attributes'
|
6
|
+
autoload :ClassMethods, 'globalize/active_record/class_methods'
|
7
|
+
autoload :Exceptions, 'globalize/active_record/exceptions'
|
8
|
+
autoload :InstanceMethods, 'globalize/active_record/instance_methods'
|
9
|
+
autoload :Migration, 'globalize/active_record/migration'
|
10
|
+
autoload :Translation, 'globalize/active_record/translation'
|
11
|
+
end
|
12
|
+
end
|
data/lib/globalize.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'patches/active_record/xml_attribute_serializer'
|
3
|
+
require 'patches/active_record/query_method'
|
4
|
+
require 'patches/active_record/uniqueness_validator'
|
5
|
+
|
6
|
+
module Globalize
|
7
|
+
autoload :ActiveRecord, 'globalize/active_record'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def locale
|
11
|
+
read_locale || I18n.locale
|
12
|
+
end
|
13
|
+
|
14
|
+
def locale=(locale)
|
15
|
+
set_locale(locale)
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_locale(locale, &block)
|
19
|
+
previous_locale = read_locale
|
20
|
+
set_locale(locale)
|
21
|
+
result = yield(locale)
|
22
|
+
set_locale(previous_locale)
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_locales(*locales, &block)
|
27
|
+
locales.flatten.map do |locale|
|
28
|
+
with_locale(locale, &block)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def fallbacks=(locales)
|
33
|
+
set_fallbacks(locales)
|
34
|
+
end
|
35
|
+
|
36
|
+
def i18n_fallbacks?
|
37
|
+
I18n.respond_to?(:fallbacks)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fallbacks(for_locale = self.locale)
|
41
|
+
read_fallbacks[for_locale] || default_fallbacks(for_locale)
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_fallbacks(for_locale = self.locale)
|
45
|
+
i18n_fallbacks? ? I18n.fallbacks[for_locale] : [for_locale.to_sym]
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def read_locale
|
51
|
+
Thread.current[:globalize_locale]
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_locale(locale)
|
55
|
+
Thread.current[:globalize_locale] = locale.try(:to_sym)
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_fallbacks
|
59
|
+
Thread.current[:fallbacks] || HashWithIndifferentAccess.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_fallbacks(locales)
|
63
|
+
fallback_hash = HashWithIndifferentAccess.new
|
64
|
+
|
65
|
+
locales.each do |key, value|
|
66
|
+
fallback_hash[key] = value.presence || [key]
|
67
|
+
end if locales.present?
|
68
|
+
|
69
|
+
Thread.current[:fallbacks] = fallback_hash
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
ActiveRecord::Base.extend(Globalize::ActiveRecord::ActMacro)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# A simple exception handler that behaves like the default exception handler
|
2
|
+
# but additionally logs missing translations to a given log.
|
3
|
+
#
|
4
|
+
# Useful for identifying missing translations during testing.
|
5
|
+
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_log_handler'
|
9
|
+
# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
|
10
|
+
# I18n.exception_handler = :missing_translations_log_handler
|
11
|
+
#
|
12
|
+
# To set up a different log file:
|
13
|
+
#
|
14
|
+
# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
|
15
|
+
# I18n.missing_translations_logger = logger
|
16
|
+
|
17
|
+
module I18n
|
18
|
+
@@missing_translations_logger = nil
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def missing_translations_logger
|
22
|
+
@@missing_translations_logger ||= begin
|
23
|
+
require 'logger' unless defined?(Logger)
|
24
|
+
Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def missing_translations_logger=(logger)
|
29
|
+
@@missing_translations_logger = logger
|
30
|
+
end
|
31
|
+
|
32
|
+
def missing_translations_log_handler(exception, locale, key, options)
|
33
|
+
if MissingTranslationData === exception
|
34
|
+
missing_translations_logger.warn(exception.message)
|
35
|
+
return exception.message
|
36
|
+
else
|
37
|
+
raise exception
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# A simple exception handler that behaves like the default exception handler
|
2
|
+
# but also raises on missing translations.
|
3
|
+
#
|
4
|
+
# Useful for identifying missing translations during testing.
|
5
|
+
#
|
6
|
+
# E.g.
|
7
|
+
#
|
8
|
+
# require 'globalize/i18n/missing_translations_raise_handler'
|
9
|
+
# I18n.exception_handler = :missing_translations_raise_handler
|
10
|
+
module I18n
|
11
|
+
class << self
|
12
|
+
def missing_translations_raise_handler(exception, locale, key, options)
|
13
|
+
raise exception
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
I18n.exception_handler = :missing_translations_raise_handler
|
19
|
+
|
20
|
+
ActionView::Helpers::TranslationHelper.module_eval do
|
21
|
+
def translate(key, options = {})
|
22
|
+
I18n.translate(key, options)
|
23
|
+
end
|
24
|
+
alias :t :translate
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_record/attribute_methods/query'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module AttributeMethods
|
5
|
+
module Query
|
6
|
+
def query_attribute(attr_name)
|
7
|
+
unless value = read_attribute(attr_name)
|
8
|
+
false
|
9
|
+
else
|
10
|
+
column = self.class.columns_hash[attr_name]
|
11
|
+
if column.nil?
|
12
|
+
|
13
|
+
# TODO submit a rails patch
|
14
|
+
|
15
|
+
# not sure what active_record tests say but i guess this should mean:
|
16
|
+
# call to_i and check zero? if the value is a Numeric or starts with
|
17
|
+
# a digit, so it can meaningfully be typecasted by to_i
|
18
|
+
|
19
|
+
# if Numeric === value || value !~ /[^0-9]/
|
20
|
+
if Numeric === value || value.to_s =~ /^[0-9]/
|
21
|
+
!value.to_i.zero?
|
22
|
+
else
|
23
|
+
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
|
24
|
+
!value.blank?
|
25
|
+
end
|
26
|
+
elsif column.number?
|
27
|
+
!value.zero?
|
28
|
+
else
|
29
|
+
!value.blank?
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'active_record/validations/uniqueness.rb'
|
2
|
+
|
3
|
+
ActiveRecord::Validations::UniquenessValidator.class_eval do
|
4
|
+
def validate_each_with_translations(record, attribute, value)
|
5
|
+
klass = record.class
|
6
|
+
if klass.translates? && klass.translated?(attribute)
|
7
|
+
if methods.include?(:build_relation) || respond_to?(:build_relation)
|
8
|
+
finder_class = klass.translation_class
|
9
|
+
table = finder_class.arel_table
|
10
|
+
|
11
|
+
relation = build_relation(finder_class, table, 'attribute_name', attribute).
|
12
|
+
and(table[:locale].eq(Globalize.locale)).
|
13
|
+
and(table[:value].eq(value)).
|
14
|
+
and(table[:translatable_type].eq(klass.name))
|
15
|
+
relation = relation.and(table[klass.reflect_on_association(:translations).foreign_key].not_eq(record.send(:id))) if record.persisted?
|
16
|
+
|
17
|
+
# TODO: add scope with translated attributes
|
18
|
+
if options[:scope]
|
19
|
+
ActiveRecord::Base.logger.warn("WARNING: SingleTableGlobalize3 does not currently support `scope` option on uniqueness validation for translated attributes.")
|
20
|
+
end
|
21
|
+
|
22
|
+
if finder_class.unscoped.where(relation).exists?
|
23
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
24
|
+
end
|
25
|
+
else
|
26
|
+
finder_class = klass.translation_class
|
27
|
+
table = finder_class.unscoped
|
28
|
+
|
29
|
+
table_name = finder_class.quoted_table_name
|
30
|
+
|
31
|
+
if value && klass.serialized_attributes.key?(attribute.to_s)
|
32
|
+
value = YAML.dump value
|
33
|
+
end
|
34
|
+
|
35
|
+
sql, params = mount_sql_and_params(finder_class, table_name, 'value', value)
|
36
|
+
|
37
|
+
relation = table.where(sql, *params).
|
38
|
+
where(:locale => Globalize.locale).
|
39
|
+
where(:attribute_name => attribute).
|
40
|
+
where(:translatable_type => klass.name)
|
41
|
+
|
42
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
43
|
+
scope_value = record.send(scope_item)
|
44
|
+
relation = relation.where(scope_item => scope_value)
|
45
|
+
end
|
46
|
+
|
47
|
+
if record.persisted?
|
48
|
+
fk = klass.reflect_on_association(:translations).options[:foreign_key]
|
49
|
+
relation = relation.where(finder_class.arel_table[fk].not_eq(record.send(:id)))
|
50
|
+
end
|
51
|
+
|
52
|
+
if relation.exists?
|
53
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
validate_each_without_translations(record, attribute, value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias_method_chain :validate_each, :translations
|
61
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_record/serializers/xml_serializer'
|
2
|
+
|
3
|
+
ActiveRecord::XmlSerializer::Attribute.class_eval do
|
4
|
+
def compute_type_with_translations
|
5
|
+
klass = @serializable.class
|
6
|
+
if klass.translates? && klass.translated_attribute_names.include?(name.to_sym)
|
7
|
+
:string
|
8
|
+
else
|
9
|
+
compute_type_without_translations
|
10
|
+
end
|
11
|
+
end
|
12
|
+
alias_method_chain :compute_type, :translations
|
13
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'globalize'
|
metadata
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: single_table_globalize3
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sven Fuchs
|
8
|
+
- Joshua Harvey
|
9
|
+
- Clemens Kofler
|
10
|
+
- John-Paul Bader
|
11
|
+
- Tomasz Stachewicz
|
12
|
+
- Philip Arndt
|
13
|
+
- Trong Tran
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: activerecord
|
21
|
+
requirement: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - '>='
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 3.0.0
|
26
|
+
type: :runtime
|
27
|
+
prerelease: false
|
28
|
+
version_requirements: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activemodel
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 3.0.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.0.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: database_cleaner
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.6.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.6.0
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: mocha
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: pathname_local
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: test_declarative
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: sqlite3
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: rake
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: rdoc
|
133
|
+
requirement: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
type: :development
|
139
|
+
prerelease: false
|
140
|
+
version_requirements: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - '>='
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
description: Rails I18n de-facto standard library for ActiveRecord 3 model/data translation
|
146
|
+
with single table.
|
147
|
+
email: trong.v.tran@gmail.com
|
148
|
+
executables: []
|
149
|
+
extensions: []
|
150
|
+
extra_rdoc_files: []
|
151
|
+
files:
|
152
|
+
- lib/generators/globalize3/migration_generator.rb
|
153
|
+
- lib/generators/globalize3/templates/migration.rb
|
154
|
+
- lib/globalize/active_record/act_macro.rb
|
155
|
+
- lib/globalize/active_record/adapter.rb
|
156
|
+
- lib/globalize/active_record/attributes.rb
|
157
|
+
- lib/globalize/active_record/class_methods.rb
|
158
|
+
- lib/globalize/active_record/exceptions.rb
|
159
|
+
- lib/globalize/active_record/instance_methods.rb
|
160
|
+
- lib/globalize/active_record/translation.rb
|
161
|
+
- lib/globalize/active_record.rb
|
162
|
+
- lib/globalize.rb
|
163
|
+
- lib/i18n/missing_translations_log_handler.rb
|
164
|
+
- lib/i18n/missing_translations_raise_handler.rb
|
165
|
+
- lib/patches/active_record/query_method.rb
|
166
|
+
- lib/patches/active_record/uniqueness_validator.rb
|
167
|
+
- lib/patches/active_record/xml_attribute_serializer.rb
|
168
|
+
- lib/single_table_globalize3/version.rb
|
169
|
+
- lib/single_table_globalize3.rb
|
170
|
+
- Gemfile
|
171
|
+
- Gemfile.lock
|
172
|
+
- LICENSE
|
173
|
+
- Rakefile
|
174
|
+
homepage: http://github.com/trongrg/single_table_globalize3
|
175
|
+
licenses: []
|
176
|
+
metadata: {}
|
177
|
+
post_install_message:
|
178
|
+
rdoc_options: []
|
179
|
+
require_paths:
|
180
|
+
- lib
|
181
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - '>='
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
requirements:
|
188
|
+
- - '>='
|
189
|
+
- !ruby/object:Gem::Version
|
190
|
+
version: '0'
|
191
|
+
requirements: []
|
192
|
+
rubyforge_project: '[none]'
|
193
|
+
rubygems_version: 2.0.0
|
194
|
+
signing_key:
|
195
|
+
specification_version: 4
|
196
|
+
summary: Rails I18n de-facto standard library for ActiveRecord 3 model/data translation
|
197
|
+
with single table
|
198
|
+
test_files: []
|