mongoid_globalize 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@mongoid-globalize --create
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activemodel', '3.1.0.rc4'
4
+ gem 'mongoid', :git => 'git://github.com/mongoid/mongoid.git'
5
+ gem 'bson_ext'
6
+
7
+ group :development, :test do
8
+ gem 'rspec'
9
+ gem 'mongoid-rspec'
10
+ gem 'database_cleaner'
11
+ gem 'ruby-debug19'
12
+ gem 'jeweler'
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,74 @@
1
+ GIT
2
+ remote: git://github.com/mongoid/mongoid.git
3
+ revision: e2540c818ebb1d37b3a8d1b7e6dd6836ce16d445
4
+ specs:
5
+ mongoid (2.1.0)
6
+ activemodel (~> 3.0)
7
+ mongo (~> 1.3)
8
+ tzinfo (~> 0.3.22)
9
+
10
+ GEM
11
+ remote: http://rubygems.org/
12
+ specs:
13
+ activemodel (3.1.0.rc4)
14
+ activesupport (= 3.1.0.rc4)
15
+ bcrypt-ruby (~> 2.1.4)
16
+ builder (~> 3.0.0)
17
+ i18n (~> 0.6)
18
+ activesupport (3.1.0.rc4)
19
+ multi_json (~> 1.0)
20
+ archive-tar-minitar (0.5.2)
21
+ bcrypt-ruby (2.1.4)
22
+ bson (1.3.1)
23
+ bson_ext (1.3.1)
24
+ builder (3.0.0)
25
+ columnize (0.3.4)
26
+ database_cleaner (0.6.7)
27
+ diff-lcs (1.1.2)
28
+ git (1.2.5)
29
+ i18n (0.6.0)
30
+ jeweler (1.6.4)
31
+ bundler (~> 1.0)
32
+ git (>= 1.2.5)
33
+ rake
34
+ linecache19 (0.5.12)
35
+ ruby_core_source (>= 0.1.4)
36
+ mongo (1.3.1)
37
+ bson (>= 1.3.1)
38
+ mongoid-rspec (1.4.4)
39
+ mongoid (~> 2.0)
40
+ rspec (~> 2)
41
+ multi_json (1.0.3)
42
+ rake (0.9.2)
43
+ rspec (2.6.0)
44
+ rspec-core (~> 2.6.0)
45
+ rspec-expectations (~> 2.6.0)
46
+ rspec-mocks (~> 2.6.0)
47
+ rspec-core (2.6.4)
48
+ rspec-expectations (2.6.0)
49
+ diff-lcs (~> 1.1.2)
50
+ rspec-mocks (2.6.0)
51
+ ruby-debug-base19 (0.11.25)
52
+ columnize (>= 0.3.1)
53
+ linecache19 (>= 0.5.11)
54
+ ruby_core_source (>= 0.1.4)
55
+ ruby-debug19 (0.11.6)
56
+ columnize (>= 0.3.1)
57
+ linecache19 (>= 0.5.11)
58
+ ruby-debug-base19 (>= 0.11.19)
59
+ ruby_core_source (0.1.5)
60
+ archive-tar-minitar (>= 0.5.2)
61
+ tzinfo (0.3.29)
62
+
63
+ PLATFORMS
64
+ ruby
65
+
66
+ DEPENDENCIES
67
+ activemodel (= 3.1.0.rc4)
68
+ bson_ext
69
+ database_cleaner
70
+ jeweler
71
+ mongoid!
72
+ mongoid-rspec
73
+ rspec
74
+ ruby-debug19
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rdoc/task'
11
+
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ task :default => :spec
18
+
19
+ Rake::RDocTask.new(:rdoc) do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'TranslationPanel'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README.rdoc')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ require 'jeweler'
28
+ Jeweler::Tasks.new do |gem|
29
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
30
+ gem.name = "mongoid_globalize"
31
+ gem.homepage = "http://github.com/Mik-die/mongoid_globalize"
32
+ gem.license = "MIT"
33
+ gem.summary = %Q{Library for translating Mongoid documents}
34
+ gem.description = %Q{Library for translating Mongoid documents, based on Globalize3 principles}
35
+ gem.email = "MikDiet@gmail.com"
36
+ gem.authors = ["Mik-die"]
37
+ # dependencies defined in Gemfile
38
+ end
39
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,23 @@
1
+ module Mongoid::Globalize
2
+ module ActMacro
3
+ # TODO: other interface of +translates+. Like this:
4
+ # translates do
5
+ # field :title
6
+ # field :visible, type: Boolean
7
+ # end
8
+ def translates(*attr_names_or_hashes)
9
+ attr_hash = attr_names_or_hashes.inject({}) do |hash,attr|
10
+ hash.merge(attr.is_a?(Hash) ? attr : {attr => String})
11
+ end
12
+ attr_hash.each do |name, type|
13
+ self.translated_attribute_names.push name.to_sym
14
+ translated_attr_accessor(name)
15
+ translation_class.field name, type: type
16
+ end
17
+ end
18
+
19
+ def fallbacks_for_empty_translations!
20
+ self.fallbacks_for_empty_translations = true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,73 @@
1
+ module Mongoid::Globalize
2
+ class Adapter
3
+ attr_accessor :record, :stash, :translations
4
+ private :record=, :stash=
5
+
6
+ def initialize(record)
7
+ self.record = record
8
+ self.stash = Attributes.new
9
+ end
10
+
11
+ def fetch_stash(locale, name)
12
+ value = stash.read(locale, name)
13
+ return value if value
14
+ return nil
15
+ end
16
+
17
+ def fetch(locale, name)
18
+ Mongoid::Globalize.fallbacks(locale).each do |fallback|
19
+ value = fetch_stash(fallback, name) || fetch_attribute(fallback, name)
20
+ unless fallbacks_for?(value)
21
+ set_metadata(value, :locale => fallback, :requested_locale => locale)
22
+ return value
23
+ end
24
+ end
25
+ return nil
26
+ end
27
+
28
+ def write(locale, name, value)
29
+ stash.write(locale, name, value)
30
+ end
31
+
32
+ def save_translations!
33
+ stash.each do |locale, attrs|
34
+ translation = record.translations.find_or_initialize_by_locale(locale.to_s)
35
+ attrs.each{ |name, value| translation[name] = value }
36
+ translation.save!
37
+ end
38
+ # TODO: it's actual for mongoid?
39
+ #record.translations.reset
40
+ reset
41
+ end
42
+
43
+ def prepare_translations!
44
+ stash.each do |locale, attrs|
45
+ translation = record.translations.find_or_initialize_by_locale(locale.to_s)
46
+ attrs.each{ |name, value| translation[name] = value }
47
+ end
48
+ end
49
+
50
+ def reset
51
+ stash.clear
52
+ end
53
+
54
+ protected
55
+ def fetch_attribute(locale, name)
56
+ translation = record.translation_for(locale)
57
+ return translation && translation.send(name)
58
+ end
59
+
60
+ def set_metadata(object, metadata)
61
+ object.translation_metadata.merge!(meta_data) if object.respond_to?(:translation_metadata)
62
+ object
63
+ end
64
+
65
+ def fallbacks_for?(object)
66
+ object.nil? || (fallbacks_for_empty_translations? && object.blank?)
67
+ end
68
+
69
+ def fallbacks_for_empty_translations?
70
+ record.fallbacks_for_empty_translations
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # Helper class for storing values per locale. Used by Mongoid::Globalize::Adapter
2
+ # to stash and cache attribute values.
3
+
4
+ module Mongoid::Globalize
5
+ class Attributes < Hash # TODO: Think about using HashWithIndifferentAccess ?
6
+ def [](locale)
7
+ locale = locale.to_sym
8
+ self[locale] = {} unless has_key?(locale)
9
+ self.fetch(locale)
10
+ end
11
+
12
+ def contains?(locale, name)
13
+ self[locale].has_key?(name.to_s)
14
+ end
15
+
16
+ def read(locale, name)
17
+ self[locale][name.to_s]
18
+ end
19
+
20
+ def write(locale, name, value)
21
+ #raise 'z' if value.nil? # TODO
22
+ self[locale][name.to_s] = value
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,64 @@
1
+ module Mongoid::Globalize
2
+ module ClassMethods
3
+ delegate :translated_locales, :to => :translation_class
4
+
5
+ # TODO: At first deal with +translated_locales+
6
+ #
7
+ #def with_locales(*locales)
8
+ # scoped.merge(translation_class.with_locales(*locales))
9
+ #end
10
+ #
11
+ #def with_translations(*locales)
12
+ # locales = translated_locales if locales.empty?
13
+ # includes(:translations).with_locales(locales).with_required_attributes
14
+ #end
15
+ #
16
+ #def with_required_attributes
17
+ # required_translated_attributes.inject(scoped) do |scope, name|
18
+ # scope.where("#{translated_column_name(name)} IS NOT NULL")
19
+ # end
20
+ #end
21
+ #
22
+ #def with_translated_attribute(name, value, locales = nil)
23
+ # locales ||= Globalize.fallbacks
24
+ # with_translations.where(
25
+ # translated_column_name(name) => value,
26
+ # translated_column_name(:locale) => Array(locales).map(&:to_s)
27
+ # )
28
+ #end
29
+
30
+ def translated?(name)
31
+ translated_attribute_names.include?(name.to_sym)
32
+ end
33
+
34
+ def required_attributes
35
+ validators.map{ |v| v.attributes if v.is_a?(ActiveModel::Validations::PresenceValidator) }.flatten.compact
36
+ end
37
+
38
+ def required_translated_attributes
39
+ translated_attribute_names & required_attributes
40
+ end
41
+
42
+ def translation_class
43
+ @translation_class ||= begin
44
+ klass = self.const_get(:Translation) rescue nil
45
+ if klass.nil?
46
+ klass = self.const_set(:Translation, Class.new(Mongoid::Globalize::DocumentTranslation))
47
+ end
48
+ klass.embedded_in name.underscore.gsub('/', '_')
49
+ klass
50
+ end
51
+ end
52
+
53
+ protected
54
+ def translated_attr_accessor(name)
55
+ define_method(:"#{name}=") do |value|
56
+ write_attribute(name, value)
57
+ end
58
+ define_method(name) do |*args|
59
+ read_attribute(name, {:locale => args.first})
60
+ end
61
+ alias_method :"#{name}_before_type_cast", name
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,30 @@
1
+ module Mongoid::Globalize
2
+ class DocumentTranslation
3
+ include Mongoid::Document
4
+ field :locale
5
+ class << self
6
+ def with_locales(*locales)
7
+ locales = locales.flatten.map(&:to_s)
8
+ where(:locale.in => locales)
9
+ end
10
+ alias with_locale with_locales
11
+
12
+ def translated_locales
13
+ # TODO
14
+ []
15
+ end
16
+
17
+ def find_or_initialize_by_locale(locale)
18
+ with_locale(locale.to_s).first || build(:locale => locale.to_s)
19
+ end
20
+ end
21
+
22
+ def locale
23
+ read_attribute(:locale).to_sym
24
+ end
25
+
26
+ def locale=(locale)
27
+ write_attribute(:locale, locale.to_s)
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,174 @@
1
+ module Mongoid::Globalize
2
+ module InstanceMethods
3
+ delegate :translated_locales, :to => :translations
4
+
5
+ def globalize
6
+ @globalize ||= Adapter.new(self)
7
+ end
8
+
9
+ def attributes
10
+ if @stop_merging_translated_attributes
11
+ super
12
+ else
13
+ @attributes = super.merge translated_attributes
14
+ @attributes
15
+ end
16
+ end
17
+
18
+ def process(attributes, *args)
19
+ with_given_locale(attributes) { super }
20
+ end
21
+
22
+ # All of this replaced by #process.. may be :)
23
+ #def write_attributes(attributes, *args)
24
+ # with_given_locale(attributes) { super }
25
+ #end
26
+
27
+ #def update_attributes!(attributes, *args)
28
+ # with_given_locale(attributes) { super }
29
+ #end
30
+ #
31
+ #def update_attributes(attributes, *args)
32
+ # with_given_locale(attributes) { super }
33
+ #end
34
+
35
+ def write_attribute(name, value, options = {})
36
+ if translated?(name)
37
+ options = {:locale => nil}.merge(options)
38
+ access = name.to_s
39
+ unless attributes[access] == value || attribute_changed?(access)
40
+ attribute_will_change! access
41
+ end
42
+ globalize.write(options[:locale] || Mongoid::Globalize.locale, name, value)
43
+ else
44
+ super(name, value)
45
+ end
46
+ end
47
+
48
+ def read_attribute(name, options = {})
49
+ options = {:translated => true, :locale => nil}.merge(options)
50
+ if translated?(name) and options[:translated]
51
+ globalize.fetch(options[:locale] || Mongoid::Globalize.locale, name)
52
+ else
53
+ super(name)
54
+ end
55
+ end
56
+
57
+ # TODO
58
+ def remove_attribute(name)
59
+ super name
60
+ end
61
+
62
+ # Mongoid documents haven't attribute_names method, so I replace +super+
63
+ # with +@attributes.keys.sort+. So this method returns only translated and
64
+ # existing attribute names (but not all available names as in AR or G3)
65
+ def attribute_names
66
+ translated_attribute_names.map(&:to_s) + @attributes.keys.sort
67
+ end
68
+
69
+ def translated?(name)
70
+ self.class.translated?(name)
71
+ end
72
+
73
+ def translated_attributes
74
+ translated_attribute_names.inject({}) do |attributes, name|
75
+ attributes.merge(name.to_s => translation.send(name))
76
+ end
77
+ end
78
+
79
+ def untranslated_attributes
80
+ attrs = {}
81
+ attribute_names.each do |name|
82
+ attrs[name] = read_attribute(name, {:translated => false})
83
+ end
84
+ attrs
85
+ end
86
+
87
+ def set_translations(options)
88
+ options.keys.each do |locale|
89
+ translation = translation_for(locale) || translations.build(:locale => locale.to_s)
90
+ translation.update_attributes!(options[locale])
91
+ end
92
+ end
93
+
94
+ def reload
95
+ translated_attribute_names.each { |name| @attributes.delete(name.to_s) }
96
+ globalize.reset
97
+ super
98
+ end
99
+
100
+ def clone
101
+ obj = super
102
+ return obj unless respond_to?(:translated_attribute_names)
103
+
104
+ # obj.instance_variable_set(:@translations, nil) if new_record?
105
+ obj.instance_variable_set(:@globalize, nil )
106
+ each_locale_and_translated_attribute do |locale, name|
107
+ obj.globalize.write(locale, name, globalize.fetch(locale, name) )
108
+ end
109
+ return obj
110
+ end
111
+
112
+ def translation
113
+ translation_for(Mongoid::Globalize.locale)
114
+ end
115
+
116
+ def translation_for(locale)
117
+ @translation_caches ||= {}
118
+ unless @translation_caches[locale]
119
+ _translation = translations.detect{|t| t.locale.to_s == locale.to_s}
120
+ _translation ||= translations.build(:locale => locale)
121
+ @translation_caches[locale] = _translation
122
+ end
123
+ @translation_caches[locale]
124
+ end
125
+
126
+ protected
127
+ def each_locale_and_translated_attribute
128
+ used_locales.each do |locale|
129
+ translated_attribute_names.each do |name|
130
+ yield locale, name
131
+ end
132
+ end
133
+ end
134
+
135
+ def used_locales
136
+ locales = globalize.stash.keys.concat(globalize.stash.keys).concat(translations.translated_locales)
137
+ locales.uniq!
138
+ locales
139
+ end
140
+
141
+ def unmerge_translations!
142
+ @stop_merging_translated_attributes = true
143
+ translated_attribute_names.each do |name|
144
+ @attributes.delete name.to_s
145
+ @changed_attributes.delete name.to_s
146
+ end
147
+ end
148
+
149
+ def create_translations!
150
+ globalize.save_translations!
151
+ clear_translations!
152
+ end
153
+
154
+ def update_translations!
155
+ unmerge_translations!
156
+ globalize.prepare_translations!
157
+ globalize.reset
158
+ end
159
+
160
+ def clear_translations!
161
+ @translation_caches = {}
162
+ @stop_merging_translated_attributes = nil
163
+ end
164
+
165
+ def with_given_locale(attributes, &block)
166
+ attributes.symbolize_keys! if attributes.respond_to?(:symbolize_keys!)
167
+ if locale = attributes.try(:delete, :locale)
168
+ Mongoid::Globalize.with_locale(locale, &block)
169
+ else
170
+ yield
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,69 @@
1
+ require 'mongoid_globalize/adapter'
2
+ require 'mongoid_globalize/act_macro'
3
+ require 'mongoid_globalize/attributes'
4
+ require 'mongoid_globalize/class_methods'
5
+ require 'mongoid_globalize/document_translation'
6
+ require 'mongoid_globalize/instance_methods'
7
+
8
+ module Mongoid::Globalize
9
+ extend ActiveSupport::Concern
10
+ included do
11
+ class_attribute :translated_attribute_names, :fallbacks_for_empty_translations
12
+ self.translated_attribute_names = []
13
+ embeds_many :translations, :class_name => translation_class.name
14
+ #:extend => HasManyExtensions
15
+ before_create :unmerge_translations!
16
+ after_create :create_translations!
17
+ before_update :update_translations!
18
+ after_create :clear_translations!
19
+
20
+ extend Mongoid::Globalize::ActMacro
21
+ end
22
+
23
+ class << self
24
+ def locale
25
+ read_locale || I18n.locale
26
+ end
27
+
28
+ def locale=(locale)
29
+ set_locale(locale)
30
+ end
31
+
32
+ def with_locale(locale, &block)
33
+ previous_locale = read_locale
34
+ set_locale(locale)
35
+ result = yield(locale)
36
+ set_locale(previous_locale)
37
+ result
38
+ end
39
+
40
+ def with_locales(*locales, &block)
41
+ locales.flatten.map do |locale|
42
+ with_locale(locale, &block)
43
+ end
44
+ end
45
+
46
+ def fallbacks?
47
+ I18n.respond_to?(:fallbacks)
48
+ end
49
+
50
+ def fallbacks(locale = self.locale)
51
+ fallbacks? ? I18n.fallbacks[locale] : [locale.to_sym]
52
+ end
53
+
54
+ protected
55
+ def read_locale
56
+ Thread.current[:globalize_locale]
57
+ end
58
+
59
+ def set_locale(locale)
60
+ Thread.current[:globalize_locale] = locale.to_sym rescue nil
61
+ end
62
+ end
63
+ #
64
+ #module HasManyExtensions
65
+ # def find_or_initialize_by_locale(locale)
66
+ # with_locale(locale.to_s).first || build(:locale => locale.to_s)
67
+ # end
68
+ #end
69
+ end