Files

UbiquoI18n::Extensions::ActiveRecord::ClassMethods

Constants

ASSOCIATION_TYPES
(Not documented)

Public Class Methods

extended(klass) click to toggle source

(Not documented)

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 466
466:         def self.extended(klass)
467:           # Ensure that the needed variables are inherited
468:           @@translatable_inheritable_instance_variables.each do |inheritable|
469:             unless eval("@#{inheritable}").nil?
470:               klass.instance_variable_set("@#{inheritable}", eval("@#{inheritable}").dup)
471:             end
472:           end
473:           
474:           # Aliases the find and count methods to apply the locale filter
475:           klass.class_eval do
476:             class << self
477:               alias_method_chain :find, :locale_filter
478:               alias_method_chain :count, :locale_filter
479:               VALID_FIND_OPTIONS << :locale_scoped << :locale_list
480:             end
481:           end
482:           
483:           # Accept the :translation_shared option when defining associations
484:           ASSOCIATION_TYPES.each do |type|
485:             klass.send("valid_keys_for_#{type}_association") << :translation_shared
486:           end
487:         end

Public Instance Methods

add_translatable_attributes(*args) click to toggle source

Used by third parties to add fields that should always be independent between different languages

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 445
445:         def add_translatable_attributes(*args)
446:           @global_translatable_attributes += args
447:         end
add_translatable_scope(condition) click to toggle source

Used by third parties to add scopes for translations updates of common fields It accepts two formats for condition:

  • A String with a sql where condition (e.g. is_active = 1)
  • A Proc that will be called with the current element argument and that should return a string (e.g. lambda{|el| “table.field = #{el.field + 1}”})
     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 458
458:         def add_translatable_scope(condition)
459:           @translatable_scopes << condition
460:         end
count_with_locale_filter(*args) click to toggle source

Applies the locale filter if needed, then performs the normal count method

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 429
429:         def count_with_locale_filter(*args)
430:           if self.is_translatable?
431:             options = args.extract_options!
432:             apply_locale_filter!(options)
433:             count_without_locale_filter(args.first || :all, options)
434:           else
435:             count_without_locale_filter(*args)
436:           end
437:         end
find_with_locale_filter(*args) click to toggle source

Applies the locale filter if needed, then performs the normal find method

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 418
418:         def find_with_locale_filter(*args)
419:           if self.is_translatable?
420:             options = args.extract_options!
421:             apply_locale_filter!(options)
422:             find_without_locale_filter(args.first, options)
423:           else
424:             find_without_locale_filter(*args)
425:           end
426:         end
inherited(klass) click to toggle source

(Not documented)

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 489
489:         def inherited(klass)
490:           super
491:           @@translatable_inheritable_instance_variables.each do |inheritable|
492:             unless eval("@#{inheritable}").nil?            
493:               klass.instance_variable_set("@#{inheritable}", eval("@#{inheritable}").dup)
494:             end
495:           end
496:         end
initialize_translation_shared(association_id) click to toggle source

Marks the association as initialized

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 402
402:         def initialize_translation_shared association_id
403:           new_association = Array(association_id)
404:           associations = instance_variable_inherited_get("initialized_translation_shared_list") || []
405:           associations +=  new_association
406:           instance_variable_inherited_set(associations, "initialized_translation_shared_list", "initialize_translation_shared")
407:         end
initialized_translation_shared_list() click to toggle source

Returns the list of associations initialized

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 397
397:         def initialized_translation_shared_list
398:           instance_variable_inherited_get("initialized_translation_shared_list")
399:         end
instance_variable_inherited_get(var_name, method_name = nil) click to toggle source

Returns the value for the var_name instance variable, or if this is nil, follow the superclass chain to ask the value

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 322
322:         def instance_variable_inherited_get(var_name, method_name = nil)
323:           method_name ||= var_name
324:           value = instance_variable_get("@#{var_name}")
325:           if value.nil? && !@really_translatable_class
326:             self.superclass.respond_to?(method_name) && self.superclass.send(method_name)
327:           else
328:             value
329:           end
330:         end
instance_variable_inherited_set(value, var_name, method_name = nil) click to toggle source

Sets the value for the var_name instance variable, or if this is nil, follow the superclass chain to set the value

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 334
334:         def instance_variable_inherited_set(value, var_name, method_name = nil)
335:           method_name ||= var_name
336:           if !@really_translatable_class
337:             self.superclass.respond_to?(method_name) && self.superclass.send(method_name, value)
338:           else
339:             instance_variable_set("@#{var_name}", value)
340:           end
341:         end
is_translatable?() click to toggle source

Returns true if the class is marked as translatable

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 344
344:         def is_translatable?
345:           instance_variable_inherited_get("translatable", "is_translatable?")
346:         end
is_translating_relations() click to toggle source

Returns true if this class is currently translating relations

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 359
359:         def is_translating_relations
360:           instance_variable_inherited_get("is_translating_relations")
361:         end
is_translating_relations=(value) click to toggle source

Sets the value of the is_translating_relations flag

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 364
364:         def is_translating_relations=(value)
365:           instance_variable_inherited_set(value, "is_translating_relations", "is_translating_relations=")
366:         end
is_translation_shared_initialized?(association_id = nil) click to toggle source

Returns true if the translation-shared association has been initialized

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 391
391:         def is_translation_shared_initialized? association_id = nil
392:           associations = initialized_translation_shared_list
393:           associations.is_a?(Array) && associations.include?(association_id)
394:         end
process_translation_shared(reflection) click to toggle source

Given a reflection, will process the :translation_shared option

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 313
313:         def process_translation_shared reflection
314:           reset_translation_shared reflection.name
315:           if reflection.options[:translation_shared]
316:             share_translations_for reflection.name
317:           end
318:         end
really_translatable_class() click to toggle source

Returns the class that really calls the translatable method

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 354
354:         def really_translatable_class
355:           instance_variable_inherited_get("really_translatable_class")
356:         end
reset_translation_shared(association_id) click to toggle source

Unmarks an association as translation-shared initialized

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 410
410:         def reset_translation_shared association_id
411:           reset_association = Array(association_id)
412:           associations = instance_variable_inherited_get("initialized_translation_shared_list") || []
413:           associations -=  reset_association
414:           instance_variable_inherited_set(associations, "initialized_translation_shared_list", "reset_translation_shared")
415:         end
share_translations_for(*associations) click to toggle source

(Not documented)

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 219
219:         def share_translations_for(*associations)
220:           return unless self.is_translatable?
221: 
222:           associations.each do |association_id|
223: 
224:             reflection = reflections[association_id]
225:             reflection.options[:translation_shared] = true
226: 
227:             unless is_translation_shared_initialized? association_id
228:               define_method "#{association_id}_with_shared_translations" do
229: 
230:                 association = self.send("#{association_id}_without_shared_translations")
231:                 is_collection = association.respond_to? :count
232:                 # the target needs to be loaded
233:                 association.inspect
234:                 # preferred locale for the associated objects
235:                 locale = Locale.current || self.locale
236: 
237:                 if is_collection && reflection.klass.is_translatable?
238:                   # build the complete proxy_target
239:                   target = association.proxy_target
240:                   contents = []
241:                   with_translations.each do |translation|
242:                     elements = translation.send("#{association_id}_without_shared_translations")
243:                     elements.each do |element|
244:                       contents << element unless contents.map(&:content_id).include?(element.content_id)
245:                     end
246:                   end
247:                   target.clear
248:                   target.concat(contents)
249: 
250:                   # now "localize" the contents
251:                   translations_to_do = {}
252:                   target.each do |element|
253:                     if !element.in_locale?(locale) && (translation = element.in_locale(locale))
254:                       translations_to_do[element] = translation
255:                     end
256:                   end
257:                   translations_to_do.each_pair do |foreign, translation|
258:                     target.delete foreign
259:                     target << translation
260:                   end
261: 
262:                   association.loaded
263: 
264:                 # it's a proxy and sometimes does not return the same as .nil?
265:                 elsif !is_collection && !association.is_a?(NilClass)
266:                   # one-sized association, not a collection
267:                   if association.class.is_translatable? && !association.in_locale?(locale)
268:                     association = association.in_locale(locale) || association
269:                   end
270: 
271:                 elsif association.is_a? NilClass
272:                   # in a has_one, with a nil association we have to look at translations
273:                   translations.map do |translation|
274:                     element = translation.send("#{association_id}_without_shared_translations")
275:                     if element
276:                       if element.class.is_translatable? && !element.in_locale?(locale)
277:                         element = element.in_locale(locale)
278:                       end
279:                       association = element
280:                       break
281:                     end
282:                   end
283: 
284:                 end
285: 
286:                 association
287:               end
288: 
289:               alias_method_chain association_id, :shared_translations
290: 
291:               # Syncs the deletion of association elements across translations
292:               add_association_callbacks(
293:                 association_id,
294:                 :after_remove => Proc.new{ |record, removed|
295:                   record.class.translating_relations do
296:                     record.translations.each do |translation|
297:                       translation.send(association_id).delete removed.with_translations
298:                     end
299:                   end
300:                 }
301:               )
302: 
303:               # Marker to avoid recursive redefinition
304:               initialize_translation_shared association_id
305: 
306:             end
307: 
308:           end
309: 
310:         end
stop_translatable_propagation() click to toggle source

Returns true if the translatable propagation has been set to stop

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 381
381:         def stop_translatable_propagation
382:           instance_variable_inherited_get("stop_translatable_propagation")
383:         end
stop_translatable_propagation=(value) click to toggle source

Setter for the stop_translatable_propagation_flag

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 386
386:         def stop_translatable_propagation=(value)
387:           instance_variable_inherited_set(value, "stop_translatable_propagation", "stop_translatable_propagation=")
388:         end
translatable(*attrs) click to toggle source

Class method for ActiveRecord that states which attributes are translatable and therefore when updated will be only updated for the current locale.

EXAMPLE:

  translatable :title, :description

possible options:

  :timestamps => set to false to avoid translatable (i.e. independent per translation) timestamps
     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 22
 22:         def translatable(*attrs)
 23:           # inherit translatable attributes
 24:           @translatable_attributes = self.translatable_attributes || []
 25: 
 26:           @really_translatable_class = self
 27:           @translatable = true
 28: 
 29:           # extract and parse options
 30:           options = attrs.extract_options!
 31:           # add attrs from this class
 32:           @translatable_attributes += attrs
 33:           
 34:           # timestamps are independent per translation unless set
 35:           @translatable_attributes += [:created_at, :updated_at] unless options[:timestamps] == false
 36:           
 37:           # try to generate the attribute setter
 38:           self.new.send(:locale=, :generate) rescue nil
 39:           if instance_methods.include?('locale=') && !instance_methods.include?('locale_with_duality=')
 40:             # give the proper behaviour to the locale setter
 41:             define_method('locale_with_duality=') do |locale|
 42:               locale = case locale
 43:               when String
 44:                 locale
 45:               else
 46:                 locale.iso_code if locale.respond_to?(:iso_code)
 47:               end
 48:               send(:locale_without_duality=, locale)
 49:             end
 50: 
 51:             alias_method_chain :locale=, :duality
 52: 
 53:           end
 54:           
 55:           unless instance_methods.include?("in_locale")
 56:             define_method('in_locale') do |*locales|
 57:               self.class.locale(*locales).first(:conditions => {:content_id => self.content_id})
 58:             end
 59:           end
 60:           
 61:           # Checks if the instance has a locale in the given a locales list
 62:           # The last parameter can be an options hash
 63:           #   :skip_any => if true, ignore items with the :any locale.
 64:           #                else, these items always return true
 65:           define_method('in_locale?') do |*asked_locales|
 66:             options = asked_locales.extract_options!
 67:             options.reverse_merge!({
 68:               :skip_any => false
 69:             })
 70:             asked_locales.map(&:to_s).include?(self.locale) ||
 71:               (!options[:skip_any] && self.locale == 'any')
 72:           end
 73: 
 74:           # usage:
 75:           # find all content in any locale: Model.locale(:all)
 76:           # find spanish content: Model.locale('es')
 77:           # find spanish or english content. If spanish and english exists, gets the spanish version. Model.locale('es', 'en')
 78:           # find all content in spanish or any other locale if spanish dosn't exist: Model.locale('es', :all)
 79:           # find all content in any locale: Model.locale(:all)
 80:           #
 81:           named_scope :locale, lambda{|*locales|
 82:             if locales.delete(:ALL)
 83:               locales << :all
 84:               ActiveSupport::Deprecation.warn('Use :all instead of :ALL in locale()', caller(5))
 85:             end
 86: 
 87:             {:locale_scoped => true, :locale_list => locales}
 88:           }
 89:                     
 90:           # usage:
 91:           # find all items of one content: Model.content(1).first
 92:           # find all items of some contents: Model.content(1,2,3)
 93:           named_scope :content, lambda{|*content_ids|
 94:             {:conditions => {:content_id => content_ids}}
 95:           }
 96: 
 97:           # usage:
 98:           # find all translations of a given content: Model.translations(content)
 99:           # will use the defined scopes to discriminate what are translations
100:           # remember it won't return 'content' itself
101:           named_scope :translations, lambda{|content|
102:             scoped_conditions = []
103:             @translatable_scopes.each do |scope|
104:                 scoped_conditions << (String === scope ? scope : scope.call(content))
105:             end
106:              translation_condition = "#{self.table_name}.content_id = ? AND #{self.table_name}.locale != ?"
107:             unless scoped_conditions.blank?
108:               translation_condition += ' AND ' + scoped_conditions.join(' AND ')
109:             end
110:             {:conditions => [translation_condition, content.content_id, content.locale]}
111:           }
112: 
113:           # Apply these named scopes to any possible already loaded subclass
114:           subclasses.each do |klass|
115:             klass.scopes.merge! scopes.slice(:locale, :translations, :content)
116:           end
117: 
118:           # Instance method to find translations
119:           define_method('translations') do
120:             self.class.translations(self)
121:           end
122: 
123:           # Returns an array containing self and its translations
124:           define_method('with_translations') do
125:             [self] + translations
126:           end
127:           
128:           # Creates a new instance of the translatable class, using the common
129:           # values from an instance sharing the same content_id
130:           # Returns a new independent instance if content_id is nil or not found
131:           # Options can be one of these:
132:           #   :copy_all => if true, will copy all the attributes from the original, even the translatable ones
133:           def translate(content_id, locale, options = {})
134:             original = find_by_content_id(content_id)
135:             new_translation = original ? original.translate(locale, options) : new
136:             new_translation.locale = locale
137:             new_translation
138:           end
139: 
140:           # Creates (saving) a new translation of self, with the common values filled in
141:           define_method('translate') do |*attrs|
142:             locale = attrs.first
143:             options = attrs.extract_options!
144:             options[:copy_all] = options[:copy_all].nil? ? true : options[:copy_all]
145: 
146:             new_translation = self.class.new
147:             new_translation.locale = locale
148: 
149:             # copy of attributes
150:             clonable_attributes = options[:copy_all] ? :attributes_except_unique_for_translation : :untranslatable_attributes
151:             self.send(clonable_attributes).each_pair do |attr, value|
152:               new_translation.send("#{attr}=", value)
153:             end
154: 
155:             # copy of relations
156:             new_translation.copy_translatable_shared_relations_from self
157:             new_translation
158:           end
159: 
160: 
161:           # Looks for defined shared relations and performs a chain-update on them
162:           define_method('copy_translatable_shared_relations_from') do |model|
163:             # here a clean environment is needed, but save Locale.current
164:             @current_locale, Locale.current = Locale.current, nil if Locale.current
165:             self.class.is_translating_relations = true
166:             begin
167:               # act on reflections where translatable == false
168:               self.class.reflections.select do |name, reflection|
169:                 reflection.options[:translation_shared] == true
170:               end.each do |association_id, reflection_values|
171:                 association_values = model.send(association_id)
172:                 record = [association_values].flatten.first
173: 
174:                 if record && record.class.is_translatable?
175: 
176:                   all_relationship_contents = []
177:                   [association_values].flatten.each do |related_element|
178:                     existing_translation = related_element.translations.locale(locale).first
179:                     if existing_translation
180:                       all_relationship_contents << existing_translation
181:                     else
182:                       all_relationship_contents << related_element
183:                     end
184:                   end
185: 
186:                 elsif record
187: 
188:                   if reflection_values.macro == :belongs_to
189:                     # we simply copy the attribute value
190:                     all_relationship_contents = [association_values]
191:                   else
192:                     raise "This behaviour is not supported by ubiquo_i18n. Either use a has_many :through to a translatable model or mark the #{record.class} model as translatable"
193:                   end
194: 
195:                 else
196:                   next
197:                 end
198: 
199:                 all_relationship_contents = all_relationship_contents.first unless association_values.is_a?(Array)
200:                 self.send(association_id.to_s + '=', all_relationship_contents)
201:                 if reflection_values.macro == :belongs_to && !new_record?
202:                   # belongs_to is not autosaved by rails when the association is not new
203:                   save
204:                 end
205:               end
206:             ensure
207:               self.class.is_translating_relations = false
208:               Locale.current = @current_locale
209:             end
210:           end
211:           
212:           define_method 'destroy_content' do 
213:             self.translations.each(&:destroy)
214:             self.destroy
215:           end
216:           
217:         end
translatable_attributes() click to toggle source

Returns a list of translatable attributes for this class

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 349
349:         def translatable_attributes
350:           instance_variable_inherited_get("translatable_attributes")
351:         end
translate(content_id, locale, options = {}) click to toggle source

Creates a new instance of the translatable class, using the common values from an instance sharing the same content_id Returns a new independent instance if content_id is nil or not found Options can be one of these:

  :copy_all => if true, will copy all the attributes from the original, even the translatable ones
     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 133
133:           def translate(content_id, locale, options = {})
134:             original = find_by_content_id(content_id)
135:             new_translation = original ? original.translate(locale, options) : new
136:             new_translation.locale = locale
137:             new_translation
138:           end
translating_relations() {|| ...} click to toggle source

Wrapper for translating relations preventing cyclical chain updates

     # File vendor/plugins/ubiquo_i18n/lib/ubiquo_i18n/extensions/active_record.rb, line 369
369:         def translating_relations
370:           unless is_translating_relations
371:             self.is_translating_relations = true
372:             begin
373:               yield
374:             ensure
375:               self.is_translating_relations = false
376:             end
377:           end
378:         end

Disabled; run with $DEBUG to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.