model_translations 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ *.swp
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jan Andersson
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,98 @@
1
+ # ModelTranslations
2
+
3
+ Minimal implementation of Globalize2 style model translations. Rails 2.2 is
4
+ required.
5
+
6
+ ## Installation
7
+
8
+ ### Gem:
9
+
10
+ in config/environment.rb:
11
+
12
+ config.gem 'model_translations', :source => 'http://gemcutter.org'
13
+
14
+ ### Plugin:
15
+
16
+ script/plugin install git://github.com/guillaumegentil/model_translations.git
17
+
18
+ ## Implementation
19
+
20
+ class Post < ActiveRecord::Base
21
+ translates :title, :text
22
+ end
23
+
24
+ Allows you to translate values for the attributes :title and :text per locale:
25
+
26
+ I18n.locale = :en
27
+ post.title # ModelTranslations rocks!
28
+ I18n.locale = :sv
29
+ post.title # Rockar fett!
30
+
31
+ In order to make this work you need to take care of creating the appropriate
32
+ database migrations manually. The migration for the above Post model could look
33
+ like this:
34
+
35
+ class CreatePosts < ActiveRecord::Migration
36
+ def self.up
37
+ create_table :posts do |t|
38
+ t.timestamps
39
+ end
40
+ create_table :post_translations do |t|
41
+ t.string :locale
42
+ t.references :post
43
+ t.string :title
44
+ t.text :text
45
+ t.timestamps
46
+ end
47
+ end
48
+ def self.down
49
+ drop_table :posts
50
+ drop_table :post_translations
51
+ end
52
+ end
53
+
54
+ To migrate from a model with existing attributes to one with translated
55
+ attributes the migration could look like this.
56
+
57
+ class RemoveTitleTextFromPosts < ActiveRecord::Migration
58
+ def self.up
59
+ [:title, :text].each do |attribute|
60
+ Post.all.each{|post| post.update_attribute(attribute, post.read_attribute(attribute)) }
61
+ remove_column :post, attribute
62
+ end
63
+ end
64
+ def self.down
65
+ add_column :post, :title, :string
66
+ add_column :post, :text, :text
67
+ [:title, :text].each do |attribute|
68
+ Post.all.each{|post| post.write_attribute(attribute, post.send(attribute)); post.save}
69
+ end
70
+ end
71
+ end
72
+
73
+ ## Advanced Querying
74
+
75
+ All models that have translations are hooked up with a has_many :translations
76
+ association for their corresponding translation table. Use this to your advantage.
77
+
78
+ Note that the following example requires Rails 2.3 since default_scope is used.
79
+
80
+ class Post < ActiveRecord::Base
81
+ translates :title, :text
82
+
83
+ default_scope :include => :translations
84
+
85
+ named_scope :translated, lambda { { :conditions => { 'post_translations.locale' => I18n.locale.to_s } } }
86
+ named_scope :ordered_by_title, :order => 'post_translations.title'
87
+ named_scope :with_title, lambda { |title| { :conditions => { 'post_translations.title' => title } } }
88
+ end
89
+
90
+ Post.translated.ordered_by_title # All posts with the current locale sorted on title
91
+ Post.with_title('My translated title') # Equivalent to Post.find_all_by_title
92
+
93
+ As you can see including the model_translations on all querys by default gives
94
+ us (apart from reducing the number of querys to the database) the possibility
95
+ of using the post_translations table for further query customization.
96
+
97
+
98
+ Copyright (c) 2008 Jan Andersson, released under the MIT license
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "model_translations"
8
+ gem.summary = "ActiveRecord model translations"
9
+ gem.description = "Minimal implementation of Globalize2 style model translations. Rails 2.2 is required."
10
+ gem.email = "thibaud@thibaud.me"
11
+ gem.homepage = "http://github.com/guillaumegentil/model_translations"
12
+ gem.authors = ["Jan Andersson", "Thibaud Guillaume-Gentil"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ gem.files.include %w(rails/init.rb)
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "model_translations #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,70 @@
1
+ module ModelTranslations
2
+ def translates(*attributes)
3
+ attributes = attributes.collect{ |attribute| attribute.to_sym }
4
+
5
+ add_translation_model_and_logic(attributes) unless included_modules.include?(InstanceMethods)
6
+ add_translatable_attributes(attributes)
7
+ end
8
+
9
+ private
10
+ def add_translation_model_and_logic(attributes)
11
+ type = self.to_s.underscore
12
+ translation_class_name = "#{self.to_s}Translation"
13
+ translation_class = Class.new(ActiveRecord::Base) { belongs_to type.to_sym }
14
+ Object.const_set(translation_class_name, translation_class)
15
+
16
+ include InstanceMethods
17
+
18
+ has_many :translations, :class_name => translation_class_name, :dependent => :delete_all , :order => 'created_at desc'
19
+
20
+ before_validation :clear_cached_translations
21
+ after_save :update_translations!
22
+ end
23
+
24
+ def add_translatable_attributes(attributes)
25
+ attributes.each do |attribute|
26
+ define_method "#{attribute}=" do |value|
27
+ translated_attributes[attribute] = value
28
+ end
29
+
30
+ define_method attribute do
31
+ Rails.cache.fetch "#{self.class.to_s.downcase}_translations.#{id}.#{attribute}.#{I18n.locale}" do
32
+ if translated_attributes[attribute]
33
+ translated_attributes[attribute]
34
+ elsif !new_record?
35
+ translation = translations.detect { |t| t.locale == I18n.locale.to_s } ||
36
+ translations.detect { |t| t.locale == I18n.default_locale.to_s } ||
37
+ translations.first
38
+ translation ? translation[attribute] : nil
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module InstanceMethods
46
+ def translated_attributes
47
+ @translated_attributes ||= {}
48
+ end
49
+
50
+ # before_validation
51
+ def clear_cached_translations
52
+ # clear related cached
53
+ translated_attributes.each do |attribute|
54
+ Rails.cache.delete "#{self.class.to_s.downcase}_translations.#{id}.#{attribute.first}.#{I18n.locale}"
55
+ end
56
+ end
57
+
58
+ # after_save
59
+ def update_translations!
60
+ unless translated_attributes.empty?
61
+ # update or create translation
62
+ translation = translations.find_or_initialize_by_locale(I18n.locale.to_s)
63
+ translated_attributes.each do |attribute, translation_string|
64
+ translation.send("#{attribute}=", translation_string)
65
+ end
66
+ translation.save!
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{model_translations}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jan Andersson", "Thibaud Guillaume-Gentil"]
12
+ s.date = %q{2009-11-16}
13
+ s.description = %q{Minimal implementation of Globalize2 style model translations. Rails 2.2 is required.}
14
+ s.email = %q{thibaud@thibaud.me}
15
+ s.extra_rdoc_files = [
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.markdown",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "init.rb",
25
+ "lib/model_translations.rb",
26
+ "model_translations.gemspec",
27
+ "rails/init.rb",
28
+ "rails/init.rb",
29
+ "test/model_translations_test.rb",
30
+ "test/test_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/guillaumegentil/model_translations}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.5}
36
+ s.summary = %q{ActiveRecord model translations}
37
+ s.test_files = [
38
+ "test/model_translations_test.rb",
39
+ "test/test_helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
+ else
48
+ end
49
+ else
50
+ end
51
+ end
52
+
@@ -0,0 +1,3 @@
1
+ require 'model_translations'
2
+
3
+ ActiveRecord::Base.send :extend, ModelTranslations
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
4
+
5
+ def setup_db
6
+ ActiveRecord::Schema.define(:version => 1) do
7
+ create_table :posts do |t|
8
+ t.timestamps
9
+ end
10
+ create_table :post_translations do |t|
11
+ t.string :locale
12
+ t.references :post
13
+ t.string :title
14
+ t.text :text
15
+ t.timestamps
16
+ end
17
+ end
18
+ end
19
+
20
+ def teardown_db
21
+ ActiveRecord::Base.connection.tables.each do |table|
22
+ ActiveRecord::Base.connection.drop_table(table)
23
+ end
24
+ end
25
+
26
+ class Post < ActiveRecord::Base
27
+ translates :title, :text
28
+ validates_presence_of :title
29
+ end
30
+
31
+ class ModelTranslationsTest < ActiveSupport::TestCase
32
+
33
+ def setup
34
+ setup_db
35
+ I18n.locale = I18n.default_locale = :en
36
+ Post.create(:title => 'English title', :text => 'Text')
37
+ end
38
+
39
+ def teardown
40
+ teardown_db
41
+ end
42
+
43
+ test "database setup" do
44
+ assert Post.count == 1
45
+ end
46
+
47
+ test "allow translation" do
48
+ I18n.locale = :sv
49
+ Post.first.update_attribute :title, 'Svensk titel'
50
+ assert_equal 'Svensk titel', Post.first.title
51
+ I18n.locale = :en
52
+ assert_equal 'English title', Post.first.title
53
+ end
54
+
55
+ test "assert fallback to default" do
56
+ assert Post.first.title == 'English title'
57
+ I18n.locale = :sv
58
+ assert Post.first.title == 'English title'
59
+ end
60
+
61
+ test "parent has_many translations" do
62
+ assert_equal PostTranslation, Post.first.translations.first.class
63
+ end
64
+
65
+ test "translations are deleted when parent is destroyed" do
66
+ I18n.locale = :sv
67
+ Post.first.update_attribute :title, 'Svensk titel'
68
+ assert_equal 2, PostTranslation.count
69
+
70
+ Post.destroy_all
71
+ assert_equal 0, PostTranslation.count
72
+ end
73
+
74
+ test 'validates_presence_of should work' do
75
+ post = Post.new
76
+ assert_equal false, post.valid?
77
+
78
+ post.title = 'English title'
79
+ assert_equal true, post.valid?
80
+ end
81
+
82
+ test 'temporary locale switch should not clear changes' do
83
+ I18n.locale = :sv
84
+ post = Post.first
85
+ post.text = 'Svensk text'
86
+ post.title.blank?
87
+ assert_equal 'Svensk text', post.text
88
+ end
89
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_support'
4
+ require 'active_support/test_case'
5
+ require 'active_record'
6
+
7
+ require File.dirname(__FILE__) + '/../lib/model_translations'
8
+ # Explicitly include the module
9
+ ActiveRecord::Base.send :extend, ModelTranslations
10
+
11
+ # mimic Rails
12
+ module Rails
13
+ def self.cache
14
+ ActiveSupport::Cache.lookup_store(:memory_store)
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: model_translations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Andersson
8
+ - Thibaud Guillaume-Gentil
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-11-16 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Minimal implementation of Globalize2 style model translations. Rails 2.2 is required.
18
+ email: thibaud@thibaud.me
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.markdown
25
+ files:
26
+ - .gitignore
27
+ - MIT-LICENSE
28
+ - README.markdown
29
+ - Rakefile
30
+ - VERSION
31
+ - init.rb
32
+ - lib/model_translations.rb
33
+ - model_translations.gemspec
34
+ - rails/init.rb
35
+ - test/model_translations_test.rb
36
+ - test/test_helper.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/guillaumegentil/model_translations
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: ActiveRecord model translations
65
+ test_files:
66
+ - test/model_translations_test.rb
67
+ - test/test_helper.rb