Module: TranslatedAttr::ActiveRecordExtensions::ClassMethods

Defined in:
lib/translated_attr/active_record_extensions.rb

Instance Method Summary collapse

Instance Method Details

#translated_attr(*attributes) ⇒ Object

Configure translated attributes. Eg:

class Post < ActiveRecord::Base
  translated_attr :title, :description
end


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/translated_attr/active_record_extensions.rb', line 60

def translated_attr(*attributes)
  make_it_translated! unless included_modules.include?(InstanceMethods)

  attributes.each do |attribute|
    # dynamic validations
    # TODO forse va sul metodo translations_for...
    validates attribute, :translation_presence => true, :translation_uniq => true

    #dynamic finders
    (class << self; self; end).class_eval do
      define_method "find_by_#{attribute}" do |value|
        self.send("find_all_by_#{attribute}".to_sym, value).first
      end

      define_method "find_all_by_#{attribute}" do |value|
        joins(:translations).
            where("#{self.to_s.tableize.singularize}_translations.locale" => "#{I18n.locale}",
                  "#{self.to_s.tableize.singularize}_translations.#{attribute}" => "#{value}").
            readonly(false)
      end

      define_method "find_or_new_by_#{attribute}" do |value|
        self.send("find_by_#{attribute}".to_sym, value) || self.new { |r| r.send("#{attribute}=", value) }
      end
    end

    # this make possible to specify getter and setter methods per locale,
    # eg: given title attribute you can use getter
    # as: title_en or title_it and setter as title_en= and title_it=
    I18n.available_locales.each do |locale|

      define_method "#{attribute}_#{locale}=" do |value|
        set_attribute(attribute, value, locale)
      end

      define_method "#{attribute}_#{locale}" do
        return localized_attributes[locale][attribute] if localized_attributes[locale][attribute]
        return if new_record?
        translations.where(:locale => "#{locale}").first.send(attribute.to_sym) rescue nil
      end

      # extension to the above dynamic finders
      (class << self; self; end).class_eval do
        define_method "find_by_#{attribute}_#{locale}" do |value|
          self.send("find_all_by_#{attribute}_#{locale}".to_sym, value).first
        end

        define_method "find_all_by_#{attribute}_#{locale}" do |value|
          joins(:translations).
              where("#{self.to_s.tableize.singularize}_translations.locale" => "#{locale}",
                    "#{self.to_s.tableize.singularize}_translations.#{attribute}" => "#{value}").
              readonly(false)
        end

        define_method "find_or_new_by_#{attribute}_#{locale}" do |value|
          self.send("find_by_#{attribute}_#{locale}".to_sym, value) ||
              self.new { |r| r.send("#{attribute}_#{locale}=", value) }
        end
      end
    end

    # attribute setter
    define_method "#{attribute}=" do |value|
      set_attribute(attribute, value)
    end

    # attribute getter
    define_method attribute do
      # return previously setted attributes if present
      return localized_attributes[I18n.locale][attribute] if localized_attributes[I18n.locale][attribute]
      return if new_record?

      # Lookup chain:
      # if translation not present in current locale,
      # use default locale, if present.
      # Otherwise use first translation
      translation = translations.detect { |t| t.locale.to_sym == I18n.locale && t[attribute] } ||
          translations.detect { |t| t.locale.to_sym == translations_default_locale && t[attribute] } ||
          translations.first

      translation ? translation[attribute] : nil
    end

    define_method "#{attribute}_before_type_cast" do
      self.send(attribute)
    end

    define_method "modified?" do
      if valid?   # force the update_translations! method
        translations.map_by_changed?.any? || changed?
      end
    end
  end
end

#translations_for(model) ⇒ Object

Configure translation model dependency. Eg:

class PostTranslation < ActiveRecord::Base
  translations_for :post
end


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/translated_attr/active_record_extensions.rb', line 39

def translations_for(model)
  belongs_to model

  validates :locale,
            :presence => true,
            :uniqueness => { :scope => "#{model}_id" },
            :inclusion => I18n.available_locales.map{ |l| l.to_s }

  # dynamic class methods
  (class << self; self; end).class_eval do
    define_method "attribute_names_for_translation" do
      attribute_names - ["id", "#{model}_id", "locale", "created_at", "updated_at"]
    end
  end
end