aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/plugins/globalize2
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/plugins/globalize2')
-rw-r--r--vendor/plugins/globalize2/LICENSE21
-rw-r--r--vendor/plugins/globalize2/README.textile86
-rw-r--r--vendor/plugins/globalize2/Rakefile39
-rw-r--r--vendor/plugins/globalize2/VERSION1
-rw-r--r--vendor/plugins/globalize2/generators/db_backend.rb0
-rw-r--r--vendor/plugins/globalize2/generators/templates/db_backend_migration.rb25
-rw-r--r--vendor/plugins/globalize2/globalize2.gemspec81
-rw-r--r--vendor/plugins/globalize2/init.rb1
-rw-r--r--vendor/plugins/globalize2/lib/globalize.rb15
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record.rb229
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb80
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb25
-rw-r--r--vendor/plugins/globalize2/lib/globalize/active_record/migration.rb44
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb41
-rw-r--r--vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb25
-rw-r--r--vendor/plugins/globalize2/test/active_record/fallbacks_test.rb102
-rw-r--r--vendor/plugins/globalize2/test/active_record/migration_test.rb118
-rw-r--r--vendor/plugins/globalize2/test/active_record/sti_translated_test.rb49
-rw-r--r--vendor/plugins/globalize2/test/active_record/translates_test.rb96
-rw-r--r--vendor/plugins/globalize2/test/active_record/translation_class_test.rb30
-rw-r--r--vendor/plugins/globalize2/test/active_record/validation_tests.rb75
-rw-r--r--vendor/plugins/globalize2/test/active_record_test.rb467
-rw-r--r--vendor/plugins/globalize2/test/all.rb2
-rw-r--r--vendor/plugins/globalize2/test/data/models.rb56
-rw-r--r--vendor/plugins/globalize2/test/data/no_globalize_schema.rb11
-rw-r--r--vendor/plugins/globalize2/test/data/schema.rb55
-rw-r--r--vendor/plugins/globalize2/test/i18n/missing_translations_test.rb36
-rw-r--r--vendor/plugins/globalize2/test/test_helper.rb76
28 files changed, 1886 insertions, 0 deletions
diff --git a/vendor/plugins/globalize2/LICENSE b/vendor/plugins/globalize2/LICENSE
new file mode 100644
index 000000000..94a6b8160
--- /dev/null
+++ b/vendor/plugins/globalize2/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2008, 2009 Joshua Harvey
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/plugins/globalize2/README.textile b/vendor/plugins/globalize2/README.textile
new file mode 100644
index 000000000..e47e9bf37
--- /dev/null
+++ b/vendor/plugins/globalize2/README.textile
@@ -0,0 +1,86 @@
+h1. Globalize2
+
+Globalize2 is the successor of Globalize for Rails.
+
+It is compatible with and builds on the new "I18n api in Ruby on Rails":http://guides.rubyonrails.org/i18n.html. and adds model translations to ActiveRecord.
+
+Globalize2 is much more lightweight and compatible than its predecessor was. Model translations in Globalize2 use default ActiveRecord features and do not limit any ActiveRecord functionality any more.
+
+h2. Requirements
+
+ActiveRecord
+I18n
+
+(or Rails > 2.2)
+
+h2. Installation
+
+To install Globalize2 with its default setup just use:
+
+<pre><code>
+script/plugin install git://github.com/joshmh/globalize2.git
+</code></pre>
+
+h2. Model translations
+
+Model translations allow you to translate your models' attribute values. E.g.
+
+<pre><code>
+class Post < ActiveRecord::Base
+ translates :title, :text
+end
+</code></pre>
+
+Allows you to values for the attributes :title and :text per locale:
+
+<pre><code>
+I18n.locale = :en
+post.title # => Globalize2 rocks!
+
+I18n.locale = :he
+post.title # => גלובאלייז2 שולט!
+</code></pre>
+
+In order to make this work, you'll need to add the appropriate translation tables. Globalize2 comes with a handy helper method to help you do this. It's called @create_translation_table!@. Here's an example:
+
+<pre><code>
+class CreatePosts < ActiveRecord::Migration
+ def self.up
+ create_table :posts do |t|
+ t.timestamps
+ end
+ Post.create_translation_table! :title => :string, :text => :text
+ end
+ def self.down
+ drop_table :posts
+ Post.drop_translation_table!
+ end
+end
+</code></pre>
+
+Note that the ActiveRecord model @Post@ must already exist and have a @translates@ directive listing the translated fields.
+
+h2. Migration from Globalize
+
+See this script by Tomasz Stachewicz: http://gist.github.com/120867
+
+h2. Changes since Globalize2 v0.1.0
+
+* The association globalize_translations has been renamed to translations.
+
+h2. Alternative Solutions
+
+* "Veger's fork":http://github.com/veger/globalize2 - uses default AR schema for the default locale, delegates to the translations table for other locales only
+* "TranslatableColumns":http://github.com/iain/translatable_columns - have multiple languages of the same attribute in a model (Iain Hecker)
+* "localized_record":http://github.com/glennpow/localized_record - allows records to have localized attributes without any modifications to the database (Glenn Powell)
+* "model_translations":http://github.com/janne/model_translations - Minimal implementation of Globalize2 style model translations (Jan Andersson)
+
+h2. Related solutions
+
+* "globalize2_versioning":http://github.com/joshmh/globalize2_versioning - acts_as_versioned style versioning for Globalize2 (Joshua Harvey)
+* "i18n_multi_locales_validations":http://github.com/ZenCocoon/i18n_multi_locales_validations - multi-locales attributes validations to validates attributes from Globalize2 translations models (Sébastien Grosjean)
+* "Globalize2 Demo App":http://github.com/svenfuchs/globalize2-demo - demo application for Globalize2 (Sven Fuchs)</li>
+* "migrate_from_globalize1":http://gist.github.com/120867 - migrate model translations from Globalize1 to Globalize2 (Tomasz Stachewicz)</li>
+* "easy_globalize2_accessors":http://github.com/astropanic/easy_globalize2_accessors - easily access (read and write) globalize2-translated fields (astropanic, Tomasz Stachewicz)</li>
+* "globalize2-easy-translate":http://github.com/bsamman/globalize2-easy-translate - adds methods to easily access or set translated attributes to your model (bsamman)</li>
+* "batch_translations":http://github.com/alvarezrilla/batch_translations - allow saving multiple Globalize2 translations in the same request (Jose Alvarez Rilla)</li>
diff --git a/vendor/plugins/globalize2/Rakefile b/vendor/plugins/globalize2/Rakefile
new file mode 100644
index 000000000..bc35dada4
--- /dev/null
+++ b/vendor/plugins/globalize2/Rakefile
@@ -0,0 +1,39 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the globalize2 plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the globalize2 plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Globalize2'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |s|
+ s.name = "globalize2"
+ s.summary = "Rails I18n: de-facto standard library for ActiveRecord data translation"
+ s.description = "Rails I18n: de-facto standard library for ActiveRecord data translation"
+ s.email = "joshmh@gmail.com"
+ s.homepage = "http://github.com/joshmh/globalize2"
+ # s.rubyforge_project = ''
+ s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader"]
+ # s.add_development_dependency ''
+ end
+ Jeweler::GemcutterTasks.new
+rescue LoadError
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
+end
diff --git a/vendor/plugins/globalize2/VERSION b/vendor/plugins/globalize2/VERSION
new file mode 100644
index 000000000..0c62199f1
--- /dev/null
+++ b/vendor/plugins/globalize2/VERSION
@@ -0,0 +1 @@
+0.2.1
diff --git a/vendor/plugins/globalize2/generators/db_backend.rb b/vendor/plugins/globalize2/generators/db_backend.rb
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/vendor/plugins/globalize2/generators/db_backend.rb
diff --git a/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb b/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
new file mode 100644
index 000000000..0f0261113
--- /dev/null
+++ b/vendor/plugins/globalize2/generators/templates/db_backend_migration.rb
@@ -0,0 +1,25 @@
+class ActsAsTaggableMigration < ActiveRecord::Migration
+ def self.up
+ create_table :globalize_translations do |t|
+ t.string :locale, :null => false
+ t.string :key, :null => false
+ t.string :translation
+ t.timestamps
+ end
+
+# TODO: FINISH DOING MIGRATION -- stopped in the middle
+
+ create_table :globalize_translations_map do |t|
+ t.string :key, :null => false
+ t.integer :translation_id, :null => false
+ end
+
+ add_index :taggings, :tag_id
+ add_index :taggings, [:taggable_id, :taggable_type]
+ end
+
+ def self.down
+ drop_table :globalize_translations
+ drop_table :tags
+ end
+end
diff --git a/vendor/plugins/globalize2/globalize2.gemspec b/vendor/plugins/globalize2/globalize2.gemspec
new file mode 100644
index 000000000..89021115e
--- /dev/null
+++ b/vendor/plugins/globalize2/globalize2.gemspec
@@ -0,0 +1,81 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{globalize2}
+ s.version = "0.2.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Sven Fuchs, Joshua Harvey, Clemens Kofler, John-Paul Bader"]
+ s.date = %q{2010-04-22}
+ s.description = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
+ s.email = %q{joshmh@gmail.com}
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.textile"
+ ]
+ s.files = [
+ ".gitignore",
+ "LICENSE",
+ "README.textile",
+ "Rakefile",
+ "VERSION",
+ "generators/db_backend.rb",
+ "generators/templates/db_backend_migration.rb",
+ "globalize2.gemspec",
+ "init.rb",
+ "lib/globalize.rb",
+ "lib/globalize/active_record.rb",
+ "lib/globalize/active_record/adapter.rb",
+ "lib/globalize/active_record/attributes.rb",
+ "lib/globalize/active_record/migration.rb",
+ "lib/i18n/missing_translations_log_handler.rb",
+ "lib/i18n/missing_translations_raise_handler.rb",
+ "test/active_record/fallbacks_test.rb",
+ "test/active_record/migration_test.rb",
+ "test/active_record/sti_translated_test.rb",
+ "test/active_record/translates_test.rb",
+ "test/active_record/translation_class_test.rb",
+ "test/active_record/validation_tests.rb",
+ "test/active_record_test.rb",
+ "test/all.rb",
+ "test/data/models.rb",
+ "test/data/no_globalize_schema.rb",
+ "test/data/schema.rb",
+ "test/i18n/missing_translations_test.rb",
+ "test/test_helper.rb"
+ ]
+ s.homepage = %q{http://github.com/joshmh/globalize2}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.6}
+ s.summary = %q{Rails I18n: de-facto standard library for ActiveRecord data translation}
+ s.test_files = [
+ "test/active_record/fallbacks_test.rb",
+ "test/active_record/migration_test.rb",
+ "test/active_record/sti_translated_test.rb",
+ "test/active_record/translates_test.rb",
+ "test/active_record/translation_class_test.rb",
+ "test/active_record/validation_tests.rb",
+ "test/active_record_test.rb",
+ "test/all.rb",
+ "test/data/models.rb",
+ "test/data/no_globalize_schema.rb",
+ "test/data/schema.rb",
+ "test/i18n/missing_translations_test.rb",
+ "test/test_helper.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ else
+ end
+ else
+ end
+end
+
diff --git a/vendor/plugins/globalize2/init.rb b/vendor/plugins/globalize2/init.rb
new file mode 100644
index 000000000..a4089251b
--- /dev/null
+++ b/vendor/plugins/globalize2/init.rb
@@ -0,0 +1 @@
+require 'globalize'
diff --git a/vendor/plugins/globalize2/lib/globalize.rb b/vendor/plugins/globalize2/lib/globalize.rb
new file mode 100644
index 000000000..67c1878ec
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/globalize.rb
@@ -0,0 +1,15 @@
+module Globalize
+ autoload :ActiveRecord, 'globalize/active_record'
+
+ class << self
+ def fallbacks?
+ I18n.respond_to?(:fallbacks)
+ end
+
+ def fallbacks(locale)
+ fallbacks? ? I18n.fallbacks[locale] : [locale.to_sym]
+ end
+ end
+end
+
+ActiveRecord::Base.send(:include, Globalize::ActiveRecord)
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record.rb b/vendor/plugins/globalize2/lib/globalize/active_record.rb
new file mode 100644
index 000000000..2915a5737
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/globalize/active_record.rb
@@ -0,0 +1,229 @@
+module Globalize
+ class MigrationError < StandardError; end
+ class MigrationMissingTranslatedField < MigrationError; end
+ class BadMigrationFieldType < MigrationError; end
+
+ module ActiveRecord
+ autoload :Adapter, 'globalize/active_record/adapter'
+ autoload :Attributes, 'globalize/active_record/attributes'
+ autoload :Migration, 'globalize/active_record/migration'
+
+ def self.included(base)
+ base.extend ActMacro
+ end
+
+ class << self
+ def build_translation_class(target, options)
+ options[:table_name] ||= "#{target.table_name.singularize}_translations"
+
+ klass = target.const_defined?(:Translation) ?
+ target.const_get(:Translation) :
+ target.const_set(:Translation, Class.new(::ActiveRecord::Base))
+
+ klass.class_eval do
+ set_table_name(options[:table_name])
+ belongs_to target.name.underscore.gsub('/', '_')
+ def locale; read_attribute(:locale).to_sym; end
+ def locale=(locale); write_attribute(:locale, locale.to_s); end
+ end
+
+ klass
+ end
+ end
+
+ module ActMacro
+ def locale
+ (defined?(@@locale) && @@locale)
+ end
+
+ def locale=(locale)
+ @@locale = locale
+ end
+
+ def translates(*attr_names)
+ return if translates?
+ options = attr_names.extract_options!
+
+ class_inheritable_accessor :translation_class, :translated_attribute_names
+ class_inheritable_writer :required_attributes
+ self.translation_class = ActiveRecord.build_translation_class(self, options)
+ self.translated_attribute_names = attr_names.map(&:to_sym)
+
+ include InstanceMethods
+ extend ClassMethods, Migration
+
+ after_save :save_translations!
+ has_many :translations, :class_name => translation_class.name,
+ :foreign_key => class_name.foreign_key,
+ :dependent => :delete_all,
+ :extend => HasManyExtensions
+
+ named_scope :with_translations, lambda { |locale|
+ conditions = required_attributes.map do |attribute|
+ "#{quoted_translation_table_name}.#{attribute} IS NOT NULL"
+ end
+ conditions << "#{quoted_translation_table_name}.locale = ?"
+ { :include => :translations, :conditions => [conditions.join(' AND '), locale] }
+ }
+
+ attr_names.each { |attr_name| translated_attr_accessor(attr_name) }
+ end
+
+ def translates?
+ included_modules.include?(InstanceMethods)
+ end
+ end
+
+ module HasManyExtensions
+ def by_locale(locale)
+ first(:conditions => { :locale => locale.to_s })
+ end
+
+ def by_locales(locales)
+ all(:conditions => { :locale => locales.map(&:to_s) })
+ end
+ end
+
+ module ClassMethods
+ delegate :set_translation_table_name, :to => :translation_class
+
+ def with_locale(locale)
+ previous_locale, self.locale = self.locale, locale
+ result = yield
+ self.locale = previous_locale
+ result
+ end
+
+ def translation_table_name
+ translation_class.table_name
+ end
+
+ def quoted_translation_table_name
+ translation_class.quoted_table_name
+ end
+
+ def required_attributes
+ @required_attributes ||= reflect_on_all_validations.select do |validation|
+ validation.macro == :validates_presence_of && translated_attribute_names.include?(validation.name)
+ end.map(&:name)
+ end
+
+ def respond_to?(method, *args, &block)
+ method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym) || super
+ end
+
+ def method_missing(method, *args)
+ if method.to_s =~ /^find_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym)
+ find_first_by_translated_attr_and_locales($1, args.first)
+ elsif method.to_s =~ /^find_all_by_(\w+)$/ && translated_attribute_names.include?($1.to_sym)
+ find_all_by_translated_attr_and_locales($1, args.first)
+ else
+ super
+ end
+ end
+
+ protected
+
+ def find_first_by_translated_attr_and_locales(name, value)
+ query = "#{translated_attr_name(name)} = ? AND #{translated_attr_name('locale')} IN (?)"
+ locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s)
+ find(
+ :first,
+ :joins => :translations,
+ :conditions => [query, value, locales],
+ :readonly => false
+ )
+ end
+
+ def find_all_by_translated_attr_and_locales(name, value)
+ query = "#{translated_attr_name(name)} = ? AND #{translated_attr_name('locale')} IN (?)"
+ locales = Globalize.fallbacks(locale || I18n.locale).map(&:to_s)
+ find(
+ :all,
+ :joins => :translations,
+ :conditions => [query, value, locales],
+ :readonly => false
+ )
+ end
+
+ def translated_attr_accessor(name)
+ define_method "#{name}=", lambda { |value|
+ globalize.write(self.class.locale || I18n.locale, name, value)
+ self[name] = value
+ }
+ define_method name, lambda { |*args|
+ globalize.fetch(args.first || self.class.locale || I18n.locale, name)
+ }
+ alias_method "#{name}_before_type_cast", name
+ end
+
+ def translated_attr_name(name)
+ "#{translation_class.table_name}.#{name}"
+ end
+ end
+
+ module InstanceMethods
+ def globalize
+ @globalize ||= Adapter.new self
+ end
+
+ def attributes
+ self.attribute_names.inject({}) do |attrs, name|
+ attrs[name] = read_attribute(name) ||
+ (globalize.fetch(I18n.locale, name) rescue nil)
+ attrs
+ end
+ end
+
+ def attributes=(attributes, *args)
+ if attributes.respond_to?(:delete) && locale = attributes.delete(:locale)
+ self.class.with_locale(locale) { super }
+ else
+ super
+ end
+ end
+
+ def attribute_names
+ translated_attribute_names.map(&:to_s) + super
+ end
+
+ def available_locales
+ translations.scoped(:select => 'DISTINCT locale').map(&:locale)
+ end
+
+ def translated_locales
+ translations.map(&:locale)
+ end
+
+ def translated_attributes
+ translated_attribute_names.inject({}) do |attributes, name|
+ attributes.merge(name => send(name))
+ end
+ end
+
+ def set_translations(options)
+ options.keys.each do |locale|
+ translation = translations.find_by_locale(locale.to_s) ||
+ translations.build(:locale => locale.to_s)
+ translation.update_attributes!(options[locale])
+ end
+ end
+
+ def reload(options = nil)
+ translated_attribute_names.each { |name| @attributes.delete(name.to_s) }
+ globalize.reset
+ super(options)
+ end
+
+ protected
+
+ def save_translations!
+ globalize.save_translations!
+ end
+ end
+ end
+end
+
+def globalize_write(name, value)
+ globalize.write(self.class.locale || I18n.locale, name, value)
+end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb b/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
new file mode 100644
index 000000000..12f89ec01
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/globalize/active_record/adapter.rb
@@ -0,0 +1,80 @@
+module Globalize
+ module ActiveRecord
+ class Adapter
+ # The cache caches attributes that already were looked up for read access.
+ # The stash keeps track of new or changed values that need to be saved.
+ attr_reader :record, :cache, :stash
+
+ def initialize(record)
+ @record = record
+ @cache = Attributes.new
+ @stash = Attributes.new
+ end
+
+ def fetch(locale, attr_name)
+ cache.contains?(locale, attr_name) ?
+ cache.read(locale, attr_name) :
+ cache.write(locale, attr_name, fetch_attribute(locale, attr_name))
+ end
+
+ def write(locale, attr_name, value)
+ stash.write(locale, attr_name, value)
+ cache.write(locale, attr_name, value)
+ end
+
+ def save_translations!
+ stash.each do |locale, attrs|
+ translation = record.translations.find_or_initialize_by_locale(locale.to_s)
+ attrs.each { |attr_name, value| translation[attr_name] = value }
+ translation.save!
+ end
+ stash.clear
+ end
+
+ def reset
+ cache.clear
+ # stash.clear
+ end
+
+ protected
+
+ def fetch_translation(locale)
+ locale = locale.to_sym
+ record.translations.loaded? ? record.translations.detect { |t| t.locale == locale } :
+ record.translations.by_locale(locale)
+ end
+
+ def fetch_translations(locale)
+ # only query if not already included with :include => translations
+ record.translations.loaded? ? record.translations :
+ record.translations.by_locales(Globalize.fallbacks(locale))
+ end
+
+ def fetch_attribute(locale, attr_name)
+ translations = fetch_translations(locale)
+ value, requested_locale = nil, locale
+
+ Globalize.fallbacks(locale).each do |fallback|
+ translation = translations.detect { |t| t.locale == fallback }
+ value = translation && translation.send(attr_name)
+ locale = fallback && break if value
+ end
+
+ set_metadata(value, :locale => locale, :requested_locale => requested_locale)
+ value
+ end
+
+ def set_metadata(object, metadata)
+ if object.respond_to?(:translation_metadata)
+ object.translation_metadata.merge!(meta_data)
+ end
+ end
+
+ def translation_metadata_accessor(object)
+ return if obj.respond_to?(:translation_metadata)
+ class << object; attr_accessor :translation_metadata end
+ object.translation_metadata ||= {}
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb b/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
new file mode 100644
index 000000000..7bd923ce2
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/globalize/active_record/attributes.rb
@@ -0,0 +1,25 @@
+# Helper class for storing values per locale. Used by Globalize::Adapter
+# to stash and cache attribute values.
+module Globalize
+ module ActiveRecord
+ class Attributes < Hash
+ def [](locale)
+ locale = locale.to_sym
+ self[locale] = {} unless has_key?(locale)
+ self.fetch(locale)
+ end
+
+ def contains?(locale, attr_name)
+ self[locale].has_key?(attr_name)
+ end
+
+ def read(locale, attr_name)
+ self[locale][attr_name]
+ end
+
+ def write(locale, attr_name, value)
+ self[locale][attr_name] = value
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb b/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
new file mode 100644
index 000000000..fa63aed8c
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/globalize/active_record/migration.rb
@@ -0,0 +1,44 @@
+module Globalize
+ module ActiveRecord
+ module Migration
+ def create_translation_table!(fields)
+ translated_attribute_names.each do |f|
+ raise MigrationMissingTranslatedField, "Missing translated field #{f}" unless fields[f]
+ end
+
+ fields.each do |name, type|
+ if translated_attribute_names.include?(name) && ![:string, :text].include?(type)
+ raise BadMigrationFieldType, "Bad field type for #{name}, should be :string or :text"
+ end
+ end
+
+ self.connection.create_table(translation_table_name) do |t|
+ t.references table_name.sub(/^#{table_name_prefix}/, "").singularize
+ t.string :locale
+ fields.each do |name, type|
+ t.column name, type
+ end
+ t.timestamps
+ end
+
+ self.connection.add_index(
+ translation_table_name,
+ "#{table_name.sub(/^#{table_name_prefix}/, "").singularize}_id",
+ :name => translation_index_name
+ )
+ end
+
+ def translation_index_name
+ require 'digest/sha1'
+ # FIXME what's the max size of an index name?
+ index_name = "index_#{translation_table_name}_on_#{self.table_name.singularize}_id"
+ index_name.size < 50 ? index_name : "index_#{Digest::SHA1.hexdigest(index_name)}"
+ end
+
+ def drop_translation_table!
+ self.connection.remove_index(translation_table_name, :name => translation_index_name) rescue nil
+ self.connection.drop_table(translation_table_name)
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
new file mode 100644
index 000000000..24c10890a
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/i18n/missing_translations_log_handler.rb
@@ -0,0 +1,41 @@
+# A simple exception handler that behaves like the default exception handler
+# but additionally logs missing translations to a given log.
+#
+# Useful for identifying missing translations during testing.
+#
+# E.g.
+#
+# require 'globalize/i18n/missing_translations_log_handler'
+# I18n.missing_translations_logger = RAILS_DEFAULT_LOGGER
+# I18n.exception_handler = :missing_translations_log_handler
+#
+# To set up a different log file:
+#
+# logger = Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
+# I18n.missing_translations_logger = logger
+
+module I18n
+ @@missing_translations_logger = nil
+
+ class << self
+ def missing_translations_logger
+ @@missing_translations_logger ||= begin
+ require 'logger' unless defined?(Logger)
+ Logger.new(STDOUT)
+ end
+ end
+
+ def missing_translations_logger=(logger)
+ @@missing_translations_logger = logger
+ end
+
+ def missing_translations_log_handler(exception, locale, key, options)
+ if MissingTranslationData === exception
+ missing_translations_logger.warn(exception.message)
+ return exception.message
+ else
+ raise exception
+ end
+ end
+ end
+end
diff --git a/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb b/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
new file mode 100644
index 000000000..18237b151
--- /dev/null
+++ b/vendor/plugins/globalize2/lib/i18n/missing_translations_raise_handler.rb
@@ -0,0 +1,25 @@
+# A simple exception handler that behaves like the default exception handler
+# but also raises on missing translations.
+#
+# Useful for identifying missing translations during testing.
+#
+# E.g.
+#
+# require 'globalize/i18n/missing_translations_raise_handler'
+# I18n.exception_handler = :missing_translations_raise_handler
+module I18n
+ class << self
+ def missing_translations_raise_handler(exception, locale, key, options)
+ raise exception
+ end
+ end
+end
+
+I18n.exception_handler = :missing_translations_raise_handler
+
+ActionView::Helpers::TranslationHelper.module_eval do
+ def translate(key, options = {})
+ I18n.translate(key, options)
+ end
+ alias :t :translate
+end
diff --git a/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb b/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
new file mode 100644
index 000000000..449ec8b2b
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/fallbacks_test.rb
@@ -0,0 +1,102 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+if I18n.respond_to?(:fallbacks)
+ class TranslatedTest < ActiveSupport::TestCase
+ def setup
+ I18n.locale = :'en-US'
+ I18n.fallbacks.clear
+ reset_db!
+ ActiveRecord::Base.locale = nil
+ end
+
+ def teardown
+ I18n.fallbacks.clear
+ end
+
+ test "keeping one field in new locale when other field is changed" do
+ I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
+ post = Post.create :subject => 'foo'
+ I18n.locale = 'de-DE'
+ post.content = 'bar'
+ assert_equal 'foo', post.subject
+ end
+
+ test "modifying non-required field in a new locale" do
+ I18n.fallbacks.map 'de-DE' => [ 'en-US' ]
+ post = Post.create :subject => 'foo'
+ I18n.locale = 'de-DE'
+ post.content = 'bar'
+ assert post.save
+ end
+
+ test "resolves a simple fallback" do
+ I18n.locale = 'de-DE'
+ post = Post.create :subject => 'foo'
+ I18n.locale = 'de'
+ post.subject = 'baz'
+ post.content = 'bar'
+ post.save
+ I18n.locale = 'de-DE'
+ assert_equal 'foo', post.subject
+ assert_equal 'bar', post.content
+ end
+
+ test "resolves a simple fallback without reloading" do
+ I18n.locale = 'de-DE'
+ post = Post.new :subject => 'foo'
+ I18n.locale = 'de'
+ post.subject = 'baz'
+ post.content = 'bar'
+ I18n.locale = 'de-DE'
+ assert_equal 'foo', post.subject
+ assert_equal 'bar', post.content
+ end
+
+ test "resolves a complex fallback without reloading" do
+ I18n.fallbacks.map 'de' => %w(en he)
+ I18n.locale = 'de'
+ post = Post.new
+ I18n.locale = 'en'
+ post.subject = 'foo'
+ I18n.locale = 'he'
+ post.subject = 'baz'
+ post.content = 'bar'
+ I18n.locale = 'de'
+ assert_equal 'foo', post.subject
+ assert_equal 'bar', post.content
+ end
+
+ test 'fallbacks with lots of locale switching' do
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
+ post = Post.create :subject => 'foo'
+
+ I18n.locale = :'de-DE'
+ assert_equal 'foo', post.subject
+
+ I18n.locale = :'en-US'
+ post.update_attribute :subject, 'bar'
+
+ I18n.locale = :'de-DE'
+ assert_equal 'bar', post.subject
+ end
+
+ test 'fallbacks with lots of locale switching' do
+ I18n.fallbacks.map :'de-DE' => [ :'en-US' ]
+ child = Child.create :content => 'foo'
+
+ I18n.locale = :'de-DE'
+ assert_equal 'foo', child.content
+
+ I18n.locale = :'en-US'
+ child.update_attribute :content, 'bar'
+
+ I18n.locale = :'de-DE'
+ assert_equal 'bar', child.content
+ end
+ end
+end
+
+# TODO should validate_presence_of take fallbacks into account? maybe we need
+# an extra validation call, or more options for validate_presence_of.
+
diff --git a/vendor/plugins/globalize2/test/active_record/migration_test.rb b/vendor/plugins/globalize2/test/active_record/migration_test.rb
new file mode 100644
index 000000000..359d811f0
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/migration_test.rb
@@ -0,0 +1,118 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+class MigrationTest < ActiveSupport::TestCase
+ def setup
+ reset_db!
+ Post.drop_translation_table!
+ end
+
+ test 'globalize table added' do
+ assert !Post.connection.table_exists?(:post_translations)
+ assert !Post.connection.index_exists?(:post_translations, :post_id)
+
+ Post.create_translation_table!(:subject => :string, :content => :text)
+ assert Post.connection.table_exists?(:post_translations)
+ assert Post.connection.index_exists?(:post_translations, :post_id)
+
+ columns = Post.connection.columns(:post_translations)
+ assert locale = columns.detect { |c| c.name == 'locale' }
+ assert_equal :string, locale.type
+ assert subject = columns.detect { |c| c.name == 'subject' }
+ assert_equal :string, subject.type
+ assert content = columns.detect { |c| c.name == 'content' }
+ assert_equal :text, content.type
+ assert post_id = columns.detect { |c| c.name == 'post_id' }
+ assert_equal :integer, post_id.type
+ assert created_at = columns.detect { |c| c.name == 'created_at' }
+ assert_equal :datetime, created_at.type
+ assert updated_at = columns.detect { |c| c.name == 'updated_at' }
+ assert_equal :datetime, updated_at.type
+ end
+
+ test 'globalize table dropped' do
+ assert !Post.connection.table_exists?( :post_translations )
+ assert !Post.connection.index_exists?( :post_translations, :post_id )
+ Post.create_translation_table! :subject => :string, :content => :text
+ assert Post.connection.table_exists?( :post_translations )
+ assert Post.connection.index_exists?( :post_translations, :post_id )
+ Post.drop_translation_table!
+ assert !Post.connection.table_exists?( :post_translations )
+ assert !Post.connection.index_exists?( :post_translations, :post_id )
+ end
+
+ test 'exception on missing field inputs' do
+ assert_raise Globalize::MigrationMissingTranslatedField do
+ Post.create_translation_table! :content => :text
+ end
+ end
+
+ test 'exception on bad input type' do
+ assert_raise Globalize::BadMigrationFieldType do
+ Post.create_translation_table! :subject => :string, :content => :integer
+ end
+ end
+
+ test "exception on bad input type isn't raised for untranslated fields" do
+ assert_nothing_raised do
+ Post.create_translation_table! :subject => :string, :content => :string, :views_count => :integer
+ end
+ end
+
+ test 'create_translation_table! should not be called on non-translated models' do
+ assert_raise NoMethodError do
+ Blog.create_translation_table! :name => :string
+ end
+ end
+
+ test 'drop_translation_table! should not be called on non-translated models' do
+ assert_raise NoMethodError do
+ Blog.drop_translation_table!
+ end
+ end
+
+ test "translation_index_name returns a readable index name when it's not longer than 50 characters" do
+ assert_equal 'index_post_translations_on_post_id', Post.send(:translation_index_name)
+ end
+
+ test "translation_index_name returns a hashed index name when it's longer than 50 characters" do
+ class UltraLongModelNameWithoutProper < ActiveRecord::Base
+ translates :foo
+ end
+ name = UltraLongModelNameWithoutProper.send(:translation_index_name)
+ assert_match /^index_[a-z0-9]{40}$/, name
+ end
+
+ test 'globalize table added when table has long name' do
+ UltraLongModelNameWithoutProper.create_translation_table!(
+ :subject => :string, :content => :text
+ )
+
+ assert UltraLongModelNameWithoutProper.connection.table_exists?(
+ :ultra_long_model_name_without_proper_translations
+ )
+ assert UltraLongModelNameWithoutProper.connection.index_exists?(
+ :ultra_long_model_name_without_proper_translations,
+ :name => UltraLongModelNameWithoutProper.send(
+ :translation_index_name
+ )
+ )
+ end
+
+ test 'globalize table dropped when table has long name' do
+ UltraLongModelNameWithoutProper.drop_translation_table!
+ UltraLongModelNameWithoutProper.create_translation_table!(
+ :subject => :string, :content => :text
+ )
+ UltraLongModelNameWithoutProper.drop_translation_table!
+
+ assert !UltraLongModelNameWithoutProper.connection.table_exists?(
+ :ultra_long_model_name_without_proper_translations
+ )
+ assert !UltraLongModelNameWithoutProper.connection.index_exists?(
+ :ultra_long_model_name_without_proper_translations,
+ :ultra_long_model_name_without_proper_id
+ )
+ end
+
+end
diff --git a/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb b/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
new file mode 100644
index 000000000..f529b8d6e
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/sti_translated_test.rb
@@ -0,0 +1,49 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+class StiTranslatedTest < ActiveSupport::TestCase
+ def setup
+ I18n.locale = :'en-US'
+ reset_db!
+ end
+
+ test "works with simple dynamic finders" do
+ foo = Child.create :content => 'foo'
+ Child.create :content => 'bar'
+ child = Child.find_by_content('foo')
+ assert_equal foo, child
+ end
+
+ test 'change attribute on globalized model' do
+ child = Child.create :content => 'foo'
+ assert_equal [], child.changed
+ child.content = 'bar'
+ assert_equal [ 'content' ], child.changed
+ child.content = 'baz'
+ assert_member 'content', child.changed
+ end
+
+ test 'change attribute on globalized model after locale switching' do
+ child = Child.create :content => 'foo'
+ assert_equal [], child.changed
+ child.content = 'bar'
+ I18n.locale = :de
+ assert_equal [ 'content' ], child.changed
+ end
+
+ test "saves all locales, even after locale switching" do
+ child = Child.new :content => 'foo'
+ I18n.locale = 'de-DE'
+ child.content = 'bar'
+ I18n.locale = 'he-IL'
+ child.content = 'baz'
+ child.save
+ I18n.locale = 'en-US'
+ child = Child.first
+ assert_equal 'foo', child.content
+ I18n.locale = 'de-DE'
+ assert_equal 'bar', child.content
+ I18n.locale = 'he-IL'
+ assert_equal 'baz', child.content
+ end
+end
diff --git a/vendor/plugins/globalize2/test/active_record/translates_test.rb b/vendor/plugins/globalize2/test/active_record/translates_test.rb
new file mode 100644
index 000000000..1831063fb
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/translates_test.rb
@@ -0,0 +1,96 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+class TranslatesTest < ActiveSupport::TestCase
+ def setup
+ I18n.locale = nil
+ ActiveRecord::Base.locale = nil
+ reset_db!
+ end
+
+ test 'defines a :locale accessors on ActiveRecord::Base' do
+ ActiveRecord::Base.locale = :de
+ assert_equal :de, ActiveRecord::Base.locale
+ end
+
+ test 'the :locale reader on ActiveRecord::Base does not default to I18n.locale (anymore)' do
+ I18n.locale = :en
+ assert_nil ActiveRecord::Base.locale
+ end
+
+ test 'ActiveRecord::Base.with_locale temporarily sets the given locale and yields the block' do
+ I18n.locale = :en
+ post = Post.with_locale(:de) do
+ Post.create!(:subject => 'Titel', :content => 'Inhalt')
+ end
+ assert_nil Post.locale
+ assert_equal :en, I18n.locale
+
+ I18n.locale = :de
+ assert_equal 'Titel', post.subject
+ end
+
+ test 'translation_class returns the Translation class' do
+ assert_equal Post::Translation, Post.translation_class
+ end
+
+ test 'defines a has_many association on the model class' do
+ assert_has_many Post, :translations
+ end
+
+ test 'defines a scope for retrieving locales that have complete translations' do
+ post = Post.create!(:subject => 'subject', :content => 'content')
+ assert_equal [:en], post.translated_locales
+ end
+
+ test 'sets the given attributes to translated_attribute_names' do
+ assert_equal [:subject, :content], Post.translated_attribute_names
+ end
+
+ test 'defines accessors for the translated attributes' do
+ post = Post.new
+ assert post.respond_to?(:subject)
+ assert post.respond_to?(:subject=)
+ end
+
+ test 'attribute reader without arguments will use the current locale on ActiveRecord::Base or I18n' do
+ post = Post.with_locale(:de) do
+ Post.create!(:subject => 'Titel', :content => 'Inhalt')
+ end
+ I18n.locale = :de
+ assert_equal 'Titel', post.subject
+
+ I18n.locale = :en
+ ActiveRecord::Base.locale = :de
+ assert_equal 'Titel', post.subject
+ end
+
+ test 'attribute reader when passed a locale will use the given locale' do
+ post = Post.with_locale(:de) do
+ Post.create!(:subject => 'Titel', :content => 'Inhalt')
+ end
+ assert_equal 'Titel', post.subject(:de)
+ end
+
+ test 'attribute reader will use the current locale on ActiveRecord::Base or I18n' do
+ post = Post.with_locale(:en) do
+ Post.create!(:subject => 'title', :content => 'content')
+ end
+ I18n.locale = :de
+ post.subject = 'Titel'
+ assert_equal 'Titel', post.subject
+
+ ActiveRecord::Base.locale = :en
+ post.subject = 'title'
+ assert_equal 'title', post.subject
+ end
+
+ test "find_by_xx records have writable attributes" do
+ Post.create :subject => "change me"
+ p = Post.find_by_subject("change me")
+ p.subject = "changed"
+ assert_nothing_raised(ActiveRecord::ReadOnlyRecord) do
+ p.save
+ end
+ end
+end
diff --git a/vendor/plugins/globalize2/test/active_record/translation_class_test.rb b/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
new file mode 100644
index 000000000..1628416d7
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/translation_class_test.rb
@@ -0,0 +1,30 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+class TranlationClassTest < ActiveSupport::TestCase
+ def setup
+ reset_db!
+ end
+
+ test 'defines a Translation class nested in the model class' do
+ assert Post.const_defined?(:Translation)
+ end
+
+ test 'defines a belongs_to association' do
+ assert_belongs_to Post::Translation, :post
+ end
+
+ test 'defines a reader for :locale that always returns a symbol' do
+ post = Post::Translation.new
+ post.write_attribute('locale', 'de')
+ assert_equal :de, post.locale
+ end
+
+ test 'defines a write for :locale that always writes a string' do
+ post = Post::Translation.new
+ post.locale = :de
+ assert_equal 'de', post.read_attribute('locale')
+ end
+end
+
+
diff --git a/vendor/plugins/globalize2/test/active_record/validation_tests.rb b/vendor/plugins/globalize2/test/active_record/validation_tests.rb
new file mode 100644
index 000000000..0148fa384
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record/validation_tests.rb
@@ -0,0 +1,75 @@
+require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/../data/models')
+
+class ValidationTest < ActiveSupport::TestCase
+ def setup
+ reset_db!
+ end
+
+ def teardown
+ Validatee.instance_variable_set(:@validate_callbacks, CallbackChain.new)
+ end
+
+ test "validates_presence_of" do
+ Validatee.class_eval { validates_presence_of :string }
+ assert !Validatee.new.valid?
+ assert Validatee.new(:string => 'foo').valid?
+ end
+
+ test "validates_confirmation_of" do
+ Validatee.class_eval { validates_confirmation_of :string }
+ assert !Validatee.new(:string => 'foo', :string_confirmation => 'bar').valid?
+ assert Validatee.new(:string => 'foo', :string_confirmation => 'foo').valid?
+ end
+
+ test "validates_acceptance_of" do
+ Validatee.class_eval { validates_acceptance_of :string, :accept => '1' }
+ assert !Validatee.new(:string => '0').valid?
+ assert Validatee.new(:string => '1').valid?
+ end
+
+ test "validates_length_of (:is)" do
+ Validatee.class_eval { validates_length_of :string, :is => 1 }
+ assert !Validatee.new(:string => 'aa').valid?
+ assert Validatee.new(:string => 'a').valid?
+ end
+
+ test "validates_format_of" do
+ Validatee.class_eval { validates_format_of :string, :with => /^\d+$/ }
+ assert !Validatee.new(:string => 'a').valid?
+ assert Validatee.new(:string => '1').valid?
+ end
+
+ test "validates_inclusion_of" do
+ Validatee.class_eval { validates_inclusion_of :string, :in => %(a) }
+ assert !Validatee.new(:string => 'b').valid?
+ assert Validatee.new(:string => 'a').valid?
+ end
+
+ test "validates_exclusion_of" do
+ Validatee.class_eval { validates_exclusion_of :string, :in => %(b) }
+ assert !Validatee.new(:string => 'b').valid?
+ assert Validatee.new(:string => 'a').valid?
+ end
+
+ test "validates_numericality_of" do
+ Validatee.class_eval { validates_numericality_of :string }
+ assert !Validatee.new(:string => 'a').valid?
+ assert Validatee.new(:string => '1').valid?
+ end
+
+ # This doesn't pass and Rails' validates_uniqueness_of implementation doesn't
+ # seem to be extensible easily. One can work around that by either defining
+ # a custom validation on the Validatee model itself, or by using validates_uniqueness_of
+ # on Validatee::Translation.
+ #
+ # test "validates_uniqueness_of" do
+ # Validatee.class_eval { validates_uniqueness_of :string }
+ # Validatee.create!(:string => 'a')
+ # assert !Validatee.new(:string => 'a').valid?
+ # assert Validatee.new(:string => 'b').valid?
+ # end
+
+ # test "validates_associated" do
+ # end
+end \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/active_record_test.rb b/vendor/plugins/globalize2/test/active_record_test.rb
new file mode 100644
index 000000000..38e247e17
--- /dev/null
+++ b/vendor/plugins/globalize2/test/active_record_test.rb
@@ -0,0 +1,467 @@
+require File.expand_path(File.dirname(__FILE__) + '/test_helper')
+require File.expand_path(File.dirname(__FILE__) + '/data/models')
+
+# Higher level tests.
+
+class ActiveRecordTest < ActiveSupport::TestCase
+ def setup
+ I18n.locale = :en
+ reset_db!
+ ActiveRecord::Base.locale = nil
+ end
+
+ def assert_translated(locale, record, names, expected)
+ I18n.locale = locale
+ assert_equal Array(expected), Array(names).map { |name| record.send(name) }
+ end
+
+ test "a translated record has translations" do
+ assert_equal [], Post.new.translations
+ end
+
+ test "saves a translated version of the record for each locale" do
+ post = Post.create(:subject => 'title')
+ I18n.locale = :de
+ post.update_attributes(:subject => 'Titel')
+
+ assert_equal 2, post.translations.size
+ assert_equal %w(de en), post.translations.map(&:locale).map(&:to_s).sort
+ assert_equal %w(Titel title), post.translations.map(&:subject).sort
+ end
+
+ test "a translated record has German translations" do
+ I18n.locale = :de
+ post = Post.create(:subject => 'foo')
+ assert_equal 1, post.translations.size
+ assert_equal [:de], post.translations.map { |t| t.locale }
+ end
+
+ test "modifiying translated fields while switching locales" do
+ post = Post.create(:subject => 'title', :content => 'content')
+ assert_equal %w(title content), [post.subject, post.content]
+
+ I18n.locale = :de
+ post.subject, post.content = 'Titel', 'Inhalt'
+
+ assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
+ assert_translated(:en, post, [:subject, :content], %w(title content))
+ assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
+
+ post.save
+ post.reload
+
+ assert_translated(:en, post, [:subject, :content], %w(title content))
+ assert_translated(:de, post, [:subject, :content], %w(Titel Inhalt))
+ end
+
+ test "attribute writers do return their argument" do
+ value = Post.new.subject = 'foo'
+ assert_equal 'foo', value
+ end
+
+ test "update_attribute succeeds with valid values" do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ post.update_attribute(:subject, 'baz')
+ assert_equal 'baz', Post.first.subject
+ end
+
+ test "update_attributes fails with invalid values" do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ assert !post.update_attributes(:subject => '')
+ assert_not_nil post.reload.attributes['subject']
+ assert_equal 'foo', post.subject
+ end
+
+ test "passing the locale to create uses the given locale" do
+ post = Post.create(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
+ assert_equal :en, I18n.locale
+ assert_nil ActiveRecord::Base.locale
+
+ I18n.locale = :de
+ assert_equal 'Titel', post.subject
+ end
+
+ test "passing the locale to attributes= uses the given locale" do
+ post = Post.create(:subject => 'title', :content => 'content')
+ post.update_attributes(:subject => 'Titel', :content => 'Inhalt', :locale => :de)
+ post.reload
+
+ assert_equal :en, I18n.locale
+ assert_nil ActiveRecord::Base.locale
+
+ assert_equal 'title', post.subject
+ I18n.locale = :de
+ assert_equal 'Titel', post.subject
+ end
+
+ test 'reload works' do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ post.subject = 'baz'
+ post.reload
+ assert_equal 'foo', post.subject
+ end
+
+ test "returns nil if no translations are found (unsaved record)" do
+ post = Post.new(:subject => 'foo')
+ assert_equal 'foo', post.subject
+ assert_nil post.content
+ end
+
+ test "returns nil if no translations are found (saved record)" do
+ post = Post.create(:subject => 'foo')
+ post.reload
+ assert_equal 'foo', post.subject
+ assert_nil post.content
+ end
+
+ test "finds a German post" do
+ post = Post.create(:subject => 'foo (en)', :content => 'bar')
+ I18n.locale = :de
+ post = Post.first
+ post.subject = 'baz (de)'
+ post.save
+ assert_equal 'baz (de)', Post.first.subject
+ I18n.locale = :en
+ assert_equal 'foo (en)', Post.first.subject
+ end
+
+ test "saves an English post and loads correctly" do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ assert post.save
+ post = Post.first
+ assert_equal 'foo', post.subject
+ assert_equal 'bar', post.content
+ end
+
+ test "returns the value for the correct locale, after locale switching" do
+ post = Post.create(:subject => 'foo')
+ I18n.locale = :de
+ post.subject = 'bar'
+ post.save
+ I18n.locale = :en
+ post = Post.first
+ assert_equal 'foo', post.subject
+ I18n.locale = :de
+ assert_equal 'bar', post.subject
+ end
+
+ test "returns the value for the correct locale, after locale switching, without saving" do
+ post = Post.create :subject => 'foo'
+ I18n.locale = :de
+ post.subject = 'bar'
+ I18n.locale = :en
+ assert_equal 'foo', post.subject
+ I18n.locale = :de
+ assert_equal 'bar', post.subject
+ end
+
+ test "saves all locales, even after locale switching" do
+ post = Post.new :subject => 'foo'
+ I18n.locale = :de
+ post.subject = 'bar'
+ I18n.locale = :he
+ post.subject = 'baz'
+ post.save
+ I18n.locale = :en
+ post = Post.first
+ assert_equal 'foo', post.subject
+ I18n.locale = :de
+ assert_equal 'bar', post.subject
+ I18n.locale = :he
+ assert_equal 'baz', post.subject
+ end
+
+ test "works with associations" do
+ blog = Blog.create
+ post1 = blog.posts.create(:subject => 'foo')
+
+ I18n.locale = :de
+ post2 = blog.posts.create(:subject => 'bar')
+ assert_equal 2, blog.posts.size
+
+ I18n.locale = :en
+ assert_equal 'foo', blog.posts.first.subject
+ assert_nil blog.posts.last.subject
+
+ I18n.locale = :de
+ assert_equal 'bar', blog.posts.last.subject
+ end
+
+ test "works with simple dynamic finders" do
+ foo = Post.create(:subject => 'foo')
+ Post.create(:subject => 'bar')
+ post = Post.find_by_subject('foo')
+ assert_equal foo, post
+ end
+
+ test 'change attribute on globalized model' do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ assert_equal [], post.changed
+ post.subject = 'baz'
+ assert_equal ['subject'], post.changed
+ post.content = 'quux'
+ assert_member 'subject', post.changed
+ assert_member 'content', post.changed
+ end
+
+ test 'change attribute on globalized model after locale switching' do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ assert_equal [], post.changed
+ post.subject = 'baz'
+ I18n.locale = :de
+ assert_equal ['subject'], post.changed
+ end
+
+ test 'complex writing and stashing' do
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ post.subject = nil
+ assert_nil post.subject
+ assert !post.valid?
+ post.subject = 'stashed_foo'
+ assert_equal 'stashed_foo', post.subject
+ end
+
+ test 'translated class locale setting' do
+ assert ActiveRecord::Base.respond_to?(:locale)
+ assert_equal :en, I18n.locale
+ assert_nil ActiveRecord::Base.locale
+
+ I18n.locale = :de
+ assert_equal :de, I18n.locale
+ assert_nil ActiveRecord::Base.locale
+
+ ActiveRecord::Base.locale = :es
+ assert_equal :de, I18n.locale
+ assert_equal :es, ActiveRecord::Base.locale
+
+ I18n.locale = :fr
+ assert_equal :fr, I18n.locale
+ assert_equal :es, ActiveRecord::Base.locale
+ end
+
+ test "untranslated class responds to locale" do
+ assert Blog.respond_to?(:locale)
+ end
+
+ test "to ensure locales in different classes are the same" do
+ ActiveRecord::Base.locale = :de
+ assert_equal :de, ActiveRecord::Base.locale
+ assert_equal :de, Parent.locale
+
+ Parent.locale = :es
+ assert_equal :es, ActiveRecord::Base.locale
+ assert_equal :es, Parent.locale
+ end
+
+ test "attribute saving goes by content locale and not global locale" do
+ ActiveRecord::Base.locale = :de
+ assert_equal :en, I18n.locale
+ Post.create :subject => 'foo'
+ assert_equal :de, Post.first.translations.first.locale
+ end
+
+ test "attribute loading goes by content locale and not global locale" do
+ post = Post.create(:subject => 'foo')
+ assert_nil ActiveRecord::Base.locale
+
+ ActiveRecord::Base.locale = :de
+ assert_equal :en, I18n.locale
+ post.update_attribute(:subject, 'foo [de]')
+ assert_equal 'foo [de]', Post.first.subject
+
+ ActiveRecord::Base.locale = :en
+ assert_equal 'foo', Post.first.subject
+ end
+
+ test "access content locale before setting" do
+ Globalize::ActiveRecord::ActMacro.class_eval "remove_class_variable(:@@locale)"
+ assert_nothing_raised { ActiveRecord::Base.locale }
+ end
+
+ test "available_locales" do
+ Post.locale = :de
+ post = Post.create(:subject => 'foo')
+ Post.locale = :es
+ post.update_attribute(:subject, 'bar')
+ Post.locale = :fr
+ post.update_attribute(:subject, 'baz')
+ assert_equal [:de, :es, :fr], post.available_locales
+ assert_equal [:de, :es, :fr], Post.first.available_locales
+ end
+
+ test "saving record correctly after post-save reload" do
+ reloader = Reloader.create(:content => 'foo')
+ assert_equal 'foo', reloader.content
+ end
+
+ test "including translations" do
+ I18n.locale = :de
+ Post.create(:subject => "Foo1", :content => "Bar1")
+ Post.create(:subject => "Foo2", :content => "Bar2")
+
+ class << Post
+ def translations_included
+ self.all(:include => :translations)
+ end
+ end
+
+ default = Post.all.map { |x| [x.subject, x.content] }
+ with_include = Post.translations_included.map { |x| [x.subject, x.content] }
+ assert_equal default, with_include
+ end
+
+ test "setting multiple translations at once with options hash" do
+ Post.locale = :de
+ post = Post.create(:subject => "foo1", :content => "foo1")
+ Post.locale = :en
+ post.update_attributes(:subject => "bar1", :content => "bar1")
+
+ options = { :de => {:subject => "foo2", :content => "foo2"},
+ :en => {:subject => "bar2", :content => "bar2"} }
+ post.set_translations options
+ post.reload
+
+ assert ["bar2", "bar2"], [post.subject, post.content]
+ Post.locale = :de
+ assert ["foo2", "foo2"], [post.subject, post.content]
+ end
+
+ test "setting only one translation with set_translations" do
+ Post.locale = :de
+ post = Post.create(:subject => "foo1", :content => "foo1")
+ Post.locale = :en
+ post.update_attributes(:subject => "bar1", :content => "bar1")
+
+ options = { :en => { :subject => "bar2", :content => "bar2" } }
+ post.set_translations options
+ post.reload
+
+ assert ["bar2", "bar2"], [post.subject, post.content]
+ Post.locale = :de
+ assert ["foo1", "foo1"], [post.subject, post.content]
+ end
+
+ test "setting only selected attributes with set_translations" do
+ Post.locale = :de
+ post = Post.create(:subject => "foo1", :content => "foo1")
+ Post.locale = :en
+ post.update_attributes(:subject => "bar1", :content => "bar1")
+
+ options = { :de => { :content => "foo2" }, :en => { :subject => "bar2" } }
+ post.set_translations options
+ post.reload
+
+ assert ["bar2", "bar1"], [post.subject, post.content]
+ Post.locale = :de
+ assert ["foo1", "foo2"], [post.subject, post.content]
+ end
+
+ test "setting invalid attributes raises ArgumentError" do
+ Post.locale = :de
+ post = Post.create(:subject => "foo1", :content => "foo1")
+ Post.locale = :en
+ post.update_attributes(:subject => "bar1", :content => "bar1")
+
+ options = { :de => {:fake => "foo2"} }
+ exception = assert_raise(ActiveRecord::UnknownAttributeError) do
+ post.set_translations options
+ end
+ assert_equal "unknown attribute: fake", exception.message
+ end
+
+ test "reload accepting find options" do
+ p = Post.create(:subject => "Foo", :content => "Bar")
+ assert p.reload(:readonly => true, :lock => true)
+ assert_raise(ArgumentError) { p.reload(:foo => :bar) }
+ end
+
+ test "dependent destroy of translation" do
+ p = Post.create(:subject => "Foo", :content => "Bar")
+ assert_equal 1, PostTranslation.count
+ p.destroy
+ assert_equal 0, PostTranslation.count
+ end
+
+ test "translating subclass of untranslated comment model" do
+ translated_comment = TranslatedComment.create(:post => @post)
+ assert_nothing_raised { translated_comment.translations }
+ end
+
+ test "modifiying translated comments works as expected" do
+ I18n.locale = :en
+ translated_comment = TranslatedComment.create(:post => @post, :content => 'foo')
+ assert_equal 'foo', translated_comment.content
+
+ I18n.locale = :de
+ translated_comment.content = 'bar'
+ assert translated_comment.save
+ assert_equal 'bar', translated_comment.content
+
+ I18n.locale = :en
+ assert_equal 'foo', translated_comment.content
+
+ assert_equal 2, translated_comment.translations.size
+ end
+
+ test "can create a proxy class for a namespaced model" do
+ assert_nothing_raised do
+ module Foo
+ module Bar
+ class Baz < ActiveRecord::Base
+ translates :bumm
+ end
+ end
+ end
+ end
+ end
+
+ test "attribute translated before type cast" do
+ Post.locale = :en
+ post = Post.create(:subject => 'foo', :content => 'bar')
+ Post.locale = :de
+ post.update_attribute(:subject, "German foo")
+ assert_equal 'German foo', post.subject_before_type_cast
+ Post.locale = :en
+ assert_equal 'foo', post.subject_before_type_cast
+ end
+
+ test "don't override existing translation class" do
+ assert PostTranslation.new.respond_to?(:existing_method)
+ end
+
+ test "has_many and named scopes work with globalize" do
+ blog = Blog.create
+ assert_nothing_raised { blog.posts.foobar }
+ end
+
+ test "required_attribuets don't include non-translated attributes" do
+ validations = [
+ stub(:name => :name, :macro => :validates_presence_of),
+ stub(:name => :email, :macro => :validates_presence_of)
+ ]
+ User.expects(:reflect_on_all_validations => validations)
+ assert_equal [:name], User.required_attributes
+ end
+
+ test "attribute_names returns translated and regular attribute names" do
+ Post.create :subject => "foo", :content => "bar"
+ assert_equal Post.last.attribute_names.sort, %w[blog_id content id subject]
+ end
+
+ test "attributes returns translated and regular attributes" do
+ Post.create :subject => "foo", :content => "bar"
+ assert_equal Post.last.attributes.keys.sort, %w[blog_id content id subject]
+ end
+
+ test "to_xml includes translated fields" do
+ Post.create :subject => "foo", :content => "bar"
+ assert Post.last.to_xml =~ /subject/
+ assert Post.last.to_xml =~ /content/
+ end
+end
+
+# TODO error checking for fields that exist in main table, don't exist in
+# proxy table, aren't strings or text
+#
+# TODO allow finding by translated attributes in conditions?
+# TODO generate advanced dynamic finders?
diff --git a/vendor/plugins/globalize2/test/all.rb b/vendor/plugins/globalize2/test/all.rb
new file mode 100644
index 000000000..ff467a176
--- /dev/null
+++ b/vendor/plugins/globalize2/test/all.rb
@@ -0,0 +1,2 @@
+files = Dir[File.dirname(__FILE__) + '/**/*_test.rb']
+files.each { |file| require file } \ No newline at end of file
diff --git a/vendor/plugins/globalize2/test/data/models.rb b/vendor/plugins/globalize2/test/data/models.rb
new file mode 100644
index 000000000..5408d6e23
--- /dev/null
+++ b/vendor/plugins/globalize2/test/data/models.rb
@@ -0,0 +1,56 @@
+#require 'ruby2ruby'
+#require 'parse_tree'
+#require 'parse_tree_extensions'
+#require 'pp'
+
+class PostTranslation < ActiveRecord::Base
+ def existing_method ; end
+end
+
+class Post < ActiveRecord::Base
+ translates :subject, :content
+ validates_presence_of :subject
+ named_scope :foobar, :conditions => { :title => "foobar" }
+end
+
+class Blog < ActiveRecord::Base
+ has_many :posts, :order => 'id ASC'
+end
+
+class Parent < ActiveRecord::Base
+ translates :content
+end
+
+class Child < Parent
+end
+
+class Comment < ActiveRecord::Base
+ validates_presence_of :content
+ belongs_to :post
+end
+
+class TranslatedComment < Comment
+ translates :content
+end
+
+class UltraLongModelNameWithoutProper < ActiveRecord::Base
+ translates :subject, :content
+ validates_presence_of :subject
+end
+
+class Reloader < Parent
+ after_create :do_reload
+
+ def do_reload
+ reload
+ end
+end
+
+class Validatee < ActiveRecord::Base
+ translates :string
+end
+
+class User < ActiveRecord::Base
+ translates :name
+ validates_presence_of :name, :email
+end
diff --git a/vendor/plugins/globalize2/test/data/no_globalize_schema.rb b/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
new file mode 100644
index 000000000..379455ddb
--- /dev/null
+++ b/vendor/plugins/globalize2/test/data/no_globalize_schema.rb
@@ -0,0 +1,11 @@
+# This schema creates tables without columns for the translated fields
+ActiveRecord::Schema.define do
+ create_table :blogs, :force => true do |t|
+ t.string :name
+ end
+
+ create_table :posts, :force => true do |t|
+ t.references :blog
+ end
+end
+
diff --git a/vendor/plugins/globalize2/test/data/schema.rb b/vendor/plugins/globalize2/test/data/schema.rb
new file mode 100644
index 000000000..910dd0855
--- /dev/null
+++ b/vendor/plugins/globalize2/test/data/schema.rb
@@ -0,0 +1,55 @@
+ActiveRecord::Schema.define do
+ create_table :blogs, :force => true do |t|
+ t.string :description
+ end
+
+ create_table :posts, :force => true do |t|
+ t.references :blog
+ end
+
+ create_table :post_translations, :force => true do |t|
+ t.string :locale
+ t.references :post
+ t.string :subject
+ t.text :content
+ end
+
+ create_table :parents, :force => true do |t|
+ end
+
+ create_table :parent_translations, :force => true do |t|
+ t.string :locale
+ t.references :parent
+ t.text :content
+ t.string :type
+ end
+
+ create_table :comments, :force => true do |t|
+ t.references :post
+ end
+
+ create_table :comment_translations, :force => true do |t|
+ t.string :locale
+ t.references :comment
+ t.string :subject
+ t.text :content
+ end
+
+ create_table :validatees, :force => true do |t|
+ end
+
+ create_table :validatee_translations, :force => true do |t|
+ t.string :locale
+ t.references :validatee
+ t.string :string
+ end
+
+ create_table :users, :force => true do |t|
+ t.string :email
+ end
+
+ create_table :users_translations, :force => true do |t|
+ t.references :user
+ t.string :name
+ end
+end
diff --git a/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb b/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
new file mode 100644
index 000000000..5d0ecd6fc
--- /dev/null
+++ b/vendor/plugins/globalize2/test/i18n/missing_translations_test.rb
@@ -0,0 +1,36 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'i18n/missing_translations_log_handler'
+
+class MissingTranslationsTest < ActiveSupport::TestCase
+ test "defines I18n.missing_translations_logger accessor" do
+ assert I18n.respond_to?(:missing_translations_logger)
+ end
+
+ test "defines I18n.missing_translations_logger= writer" do
+ assert I18n.respond_to?(:missing_translations_logger=)
+ end
+end
+
+class TestLogger < String
+ def warn(msg) self.concat msg; end
+end
+
+class LogMissingTranslationsTest < ActiveSupport::TestCase
+ def setup
+ @locale, @key, @options = :en, :foo, {}
+ @exception = I18n::MissingTranslationData.new(@locale, @key, @options)
+
+ @logger = TestLogger.new
+ I18n.missing_translations_logger = @logger
+ end
+
+ test "still returns the exception message for MissingTranslationData exceptions" do
+ result = I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
+ assert_equal 'translation missing: en, foo', result
+ end
+
+ test "logs the missing translation to I18n.missing_translations_logger" do
+ I18n.send(:missing_translations_log_handler, @exception, @locale, @key, @options)
+ assert_equal 'translation missing: en, foo', @logger
+ end
+end
diff --git a/vendor/plugins/globalize2/test/test_helper.rb b/vendor/plugins/globalize2/test/test_helper.rb
new file mode 100644
index 000000000..99a5d3950
--- /dev/null
+++ b/vendor/plugins/globalize2/test/test_helper.rb
@@ -0,0 +1,76 @@
+$LOAD_PATH << File.expand_path( File.dirname(__FILE__) + '/../lib' )
+
+require 'rubygems'
+require 'test/unit'
+require 'active_record'
+require 'active_support'
+require 'active_support/test_case'
+require 'mocha'
+require 'globalize'
+# require 'validation_reflection'
+
+config = { :adapter => 'sqlite3', :database => ':memory:' }
+ActiveRecord::Base.establish_connection(config)
+
+class ActiveSupport::TestCase
+ def reset_db!(schema_path = nil)
+ schema_path ||= File.expand_path(File.dirname(__FILE__) + '/data/schema.rb')
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Base.silence { load(schema_path) }
+ end
+
+ def assert_member(item, array)
+ assert_block "Item #{item} is not in array #{array}" do
+ array.member?(item)
+ end
+ end
+
+ def assert_belongs_to(model, associated)
+ assert model.reflect_on_all_associations(:belongs_to).detect { |association|
+ association.name.to_s == associated.to_s
+ }
+ end
+
+ def assert_has_many(model, associated)
+ assert model.reflect_on_all_associations(:has_many).detect { |association|
+ association.name.to_s == associated.to_s
+ }
+ end
+end
+
+module ActiveRecord
+ module ConnectionAdapters
+ class AbstractAdapter
+ def index_exists?(table_name, column_name)
+ indexes(table_name).any? { |index| index.name == index_name(table_name, column_name) }
+ end
+ end
+ end
+end
+
+# module ActiveRecord
+# class BaseWithoutTable < Base
+# self.abstract_class = true
+#
+# def create_or_update
+# errors.empty?
+# end
+#
+# class << self
+# def columns()
+# @columns ||= []
+# end
+#
+# def column(name, sql_type = nil, default = nil, null = true)
+# columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
+# reset_column_information
+# end
+#
+# # Do not reset @columns
+# def reset_column_information
+# read_methods.each { |name| undef_method(name) }
+# @column_names = @columns_hash = @content_columns = @dynamic_methods_hash = @read_methods = nil
+# end
+# end
+# end
+# end \ No newline at end of file