module Voltron::Translatable
Public Instance Methods
translates(*attributes)
click to toggle source
Calls superclass method
# File lib/voltron/translatable.rb, line 4 def translates(*attributes) include InstanceMethods options = (attributes.extract_options!).with_indifferent_access locales = Array.wrap(options[:locales] || Voltron.config.translate.locales).map { |l| l.to_s.underscore } attributes.each do |attrib| column = self.columns_hash[attrib.to_s] raise ::ActiveRecord::UnknownAttributeError.new(self.new, attrib) if column.nil? raise ::Voltron::Translate::InvalidColumnTypeError.new("Invalid type '#{column.type}' for attribute: #{attrib}. Translations only work on string and text attribute types.") unless [:string, :text].include?(column.type) # Override the attribute with a method that accepts a specific locale as an argument # If specified, will attempt to fetch that locale's translation, otherwise the default # locale specified for the attribute, and ultimately the current locale translation # If still nil, returns the value from super define_method :"#{attrib}" do |locale=nil| # +action_view/helpers/tags+ exist when this method is called from within # ActionView::Helpers. In other words, form helper tags. In that # case we want the actual value of the attribute, not whatever the locale is return super() if caller.any? { |l| /action_view\/helpers\/tags/.match(l) } || !Voltron.config.translate.enabled? try(:"#{attrib}_#{locale.to_s.underscore}") || try(:"#{attrib}_#{options[:default].to_s.underscore}") || try(:"#{attrib}_#{I18n.locale.to_s.underscore}") || super() end locales.each do |locale| attribute :"#{attrib}_#{locale}" # Define setter, i.e. - +attribute_es=+ define_method :"#{attrib}_#{locale}=" do |val| attribute_will_change! "#{attrib}_#{locale}" instance_variable_set("@#{attrib}_#{locale}", val) end # Define getter, i.e - +attribute_es+ # If nil, calling this method will attempt to fetch the value # We do this to avoid preloading the translations association records define_method :"#{attrib}_#{locale}" do if instance_variable_get("@#{attrib}_#{locale}").nil? instance_variable_set("@#{attrib}_#{locale}", send(:"#{attrib}_#{locale}_was")) end instance_variable_get("@#{attrib}_#{locale}") end # Define the changed? method, i.e. - +attribute_es_changed?+ define_method :"#{attrib}_#{locale}_changed?" do changed.include?("#{attrib}_#{locale}") end # Define the was method, i.e. - +attribute_es_was+ define_method :"#{attrib}_#{locale}_was" do translations.find_by(attribute_name: attrib, locale: locale).try(:translation) end define_method :"#{attrib}_#{locale}_will_change!" do attribute_will_change! "#{attrib}_#{locale}" end define_method :"#{attrib}_#{locale}?" do instance_variable_get("@#{attrib}_#{locale}").present? end end end # In case +translates+ was called multiple times, merge in the new attributes/locales # with the pre-existing ones all_attributes = @_translations.try(:keys) || [] all_attributes += attributes all_attributes.uniq! all_locales = @_translations.try(:values) || [] all_locales += locales all_locales.flatten! all_locales.uniq! has_many :translations, as: :resource, class_name: 'Voltron::Translation', dependent: :destroy before_save :build_translations accepts_nested_attributes_for :translations, reject_if: :all_blank, allow_destroy: true @_translations = all_attributes.map { |a| { a.to_s => all_locales } }.reduce(Hash.new, :merge) end