efficient_translations 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in babel.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ efficient_translations (0.0.1)
5
+ activerecord (~> 2.3)
6
+ activesupport (~> 2.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activerecord (2.3.11)
12
+ activesupport (= 2.3.11)
13
+ activesupport (2.3.11)
14
+ diff-lcs (1.1.3)
15
+ rake (0.9.2.2)
16
+ rspec (2.7.0)
17
+ rspec-core (~> 2.7.0)
18
+ rspec-expectations (~> 2.7.0)
19
+ rspec-mocks (~> 2.7.0)
20
+ rspec-core (2.7.1)
21
+ rspec-expectations (2.7.0)
22
+ diff-lcs (~> 1.1.2)
23
+ rspec-mocks (2.7.0)
24
+ sqlite3 (1.3.5)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ efficient_translations!
31
+ rake
32
+ rspec
33
+ sqlite3
data/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # EfficientTranslations
2
+
3
+ EfficientTranslations is a translation library for ActiveRecord models in Rails 2
4
+
5
+ I wrote EfficientTranslations because I'm working on several legacy rails apps with performance problems and I cannot migrate to Rails 3.
6
+
7
+ EfficientTranslations is inspired to both [Globalize2](https://github.com/joshmh/globalize2) (for models architecture: translations are stored in a separated table) and [Puret](https://github.com/jo/puret) (for cache mechanics).
8
+
9
+
10
+ ## Requirements
11
+
12
+ - ActiveSupport 2.3.x
13
+ - ActiveRecord 2.3.x
14
+
15
+
16
+ ## Architecture
17
+
18
+ The idea is always the same. One table for model records, another table for model translation records.
19
+ This architecture works well if app languages could change in the future and you don't want to add a column each time a new language is added, or if you have a large number of translations to manage.
20
+
21
+ EfficientTranslations is designed to reduce the number of queries done to get model+translations and the amount of data retrieved.
22
+
23
+ I don't think it's a perfect solution, indeed *I think it's far to be a perfect solution*, but it's a step forward.
24
+
25
+
26
+ ## Translate a Model
27
+
28
+ To explain the gem usage we'll use the following use case:
29
+
30
+ We need a Product model with two localizable fields:
31
+ - name (string)
32
+ - another_field (integer)
33
+
34
+ ### Migrations
35
+
36
+ You can create the translation table using the helper *create_translation_table*. The following:
37
+
38
+ create_table :products do |t|
39
+ t.timestamps
40
+ end
41
+ create_translation_table :products, :name => :string, :another_field => :integer
42
+
43
+ is equivalent to:
44
+
45
+ create_table :products do |t|
46
+ t.timestamps
47
+ end
48
+ create_table :product_translations do |t|
49
+ t.references :products, :null => false
50
+ t.string :locale , :null => false
51
+ t.string :name
52
+ t.integer :another_field
53
+ end
54
+
55
+
56
+ #### Models
57
+
58
+ Now we have to modify our Product model as the following:
59
+
60
+ class Prouct < ActiveRecord::Base
61
+ translates :name, :another_field
62
+ end
63
+
64
+ Done! You have the EfficientTranslations power in your hands :-)
65
+
66
+
67
+ ## Usage
68
+
69
+ ### Manage Translations
70
+
71
+ product = Product.new
72
+ I18n.default_locale = :en
73
+ I18n.locale = :en
74
+
75
+ product.name_translation :en # => nil
76
+ # .name is a wrapper to .name_translation I18n.locale
77
+ product.name # => nil
78
+ product.set_name_translation :en, 'Efficient Translations'
79
+ # no sql query is executed. When possible, a local collection is used
80
+ product.name # => 'Efficient Translations'
81
+
82
+ I18n.locale = :it
83
+ # when the current locale is not found, the default locale will be used
84
+ product.name # => 'Efficient Translations'
85
+
86
+ # .name= is a wrapper to #set_name_translation I18n.locale
87
+ product.name = 'Traduzioni Efficienti'
88
+ product.name # => 'Traduzioni Efficienti'
89
+
90
+ product.name_translations # => { :en => 'Efficient Translations', :it => 'Traduzioni Efficienti' }
91
+
92
+ # translations are saved in the db
93
+ product.save!
94
+
95
+ # Create a product using nested attributes
96
+ Product.create! :translations_attributes => [{:locale => I18n.locale.to_s, :name => 'Another'}]
97
+ Product.last.name # => 'Another'
98
+
99
+ ### Validators
100
+
101
+ The validator *validates_presence_of_default_locale* is provided to prevent a model to be saved without a translation for the default locale. Eg:
102
+
103
+ class Product < ActiveRecord::Base
104
+ translates :name, :another_field
105
+ validates_presence_of_default_locale
106
+ end
107
+
108
+ ### Named Scopes and Performances Overview
109
+
110
+ Three named scopes are defined:
111
+
112
+ #### with_translations:
113
+
114
+ # Fetch products with all translations
115
+ Product.with_translations
116
+
117
+ This will include all the translations record. So in the case you have a product with translations for :en and :it.
118
+
119
+ p = Product.with_translations.first
120
+ p.name # No sql is executed
121
+ p.name_translation :it # No sql is executed
122
+ p.name_translation :fr # No sql is executed
123
+
124
+ #### with_current_translations:
125
+
126
+ # Fetch products with translations for I18n.locale or I18n.default_locale
127
+ Product.with_current_translation
128
+
129
+ This scope will fetch only the translations you usually need when you fetch your models.
130
+ It's not perfect. Observe the following code to understand why:
131
+
132
+ Product.create! :translation_attributes => [
133
+ { :locale => :en, :name => 'Product1' },
134
+ { :locale => :it, :name => 'Prodotto1' }
135
+ ]
136
+ Product.create! :translation_attributes => [
137
+ { :locale => :it, :name => 'Prodotto2' }
138
+ ]
139
+ I18n.locale = :en
140
+
141
+ # The second product is not included in the result because it doesn't have the I18n.locale or I18n.default_locale translation
142
+ # To prevent this you can use validates_presence_of_default_locale
143
+ Product.with_current_translation.size # => 1
144
+
145
+ p = Product.with_current_translation.first
146
+ p.name # => 'Product1'; No qury is executed because we used the named scope
147
+ # translations collection doesn't contain the 'it' value, so all calls to 'it' translations will return
148
+ # the I18n.default_locale value
149
+ p.name_translation :it # => 'Product2'
150
+
151
+ # To fetch the 'it' value you have to do the following:
152
+ p.translations true # reload all translations
153
+ p.name_translation :it # => 'Prodotto1'
154
+
155
+ #### with_translation_for
156
+
157
+ This scope behaves like *with_current_translation* but it will use, in order, a locale of your choice or I18n.default_locale to fetch the translations
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "Run those specs"
6
+ task :spec do
7
+ RSpec::Core::RakeTask.new(:spec) do |t|
8
+ t.rspec_opts = %w{--colour --format progress}
9
+ t.pattern = 'spec/**/*_spec.rb'
10
+ t.rspec_path = 'bundle exec rspec'
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ if RUBY_VERSION == '1.8.7'
3
+ $:.unshift File.expand_path("../lib", __FILE__)
4
+ require "efficient_translations/version"
5
+ else
6
+ # ruby 1.9
7
+ require File.expand_path('../lib/efficient_translations/version', __FILE__)
8
+ end
9
+
10
+ Gem::Specification.new do |gem|
11
+ gem.authors = ['Nicola Racco']
12
+ gem.email = ['nicola@nicolaracco.com']
13
+ gem.description = %q{Translation library for ActiveRecord models in Rails 2}
14
+ gem.summary = %q{Translation library for ActiveRecord models in Rails 2 with an eye on performances}
15
+ gem.homepage = ''
16
+
17
+ gem.add_development_dependency 'rake'
18
+ gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'sqlite3'
20
+ gem.add_dependency 'activerecord' , '~> 2.3'
21
+ gem.add_dependency 'activesupport', '~> 2.3'
22
+
23
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ gem.files = `git ls-files`.split("\n")
25
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ gem.name = "efficient_translations"
27
+ gem.require_paths = ["lib"]
28
+ gem.version = EfficientTranslations::VERSION
29
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_support'
2
+ require 'active_record'
3
+
4
+ dir = File.join File.expand_path(File.dirname __FILE__), 'efficient_translations'
5
+ require File.join dir, 'version'
6
+ require File.join dir, 'schema'
7
+ require File.join dir, 'translation_factory'
8
+ require File.join dir, 'translates_method'
9
+
10
+ ::ActiveRecord::ConnectionAdapters::AbstractAdapter.send :include, EfficientTranslations::Schema
11
+ ::ActiveRecord::Base.send :include, EfficientTranslations::TranslatesMethod
@@ -0,0 +1,23 @@
1
+ module EfficientTranslations
2
+ # Holds schema information. To use it, just include its methods
3
+ # and overwrite the apply_schema method
4
+ module Schema
5
+ # Create the translation table for the given model
6
+ # It creates a table named <model_name>_translations
7
+ # translation_fields should contain an Hash that specify
8
+ # the column name to create and its type.
9
+ # eg. create_translation_table :product, :name => :string, :description => :string
10
+ def create_translation_table model_name, translation_fields
11
+ translation_table_name = "#{model_name.to_s}_translations"
12
+ create_table translation_table_name do |t|
13
+ t.references model_name, :null => false
14
+ t.string :locale , :null => false
15
+ end
16
+ translation_fields.each do |name, type|
17
+ add_column translation_table_name, name.to_s, type.to_sym
18
+ end
19
+ add_index translation_table_name, "#{model_name}_id"
20
+ add_index translation_table_name, ["#{model_name}_id", 'locale'], :unique => true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,122 @@
1
+ module EfficientTranslations
2
+ module TranslatesMethod
3
+ def self.included base
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def translates *field_names
9
+ make_efficient_translatable! unless defined?(translation_model)
10
+ field_names.each { |field| define_translation_accessors field }
11
+ end
12
+
13
+ def validates_presence_of_default_locale
14
+ validate :default_locale_required
15
+ end
16
+
17
+ private
18
+
19
+ def make_efficient_translatable!
20
+ cattr_accessor :translation_model
21
+ self.translation_model = TranslationFactory::new_model self
22
+
23
+ has_many :translations, :class_name => translation_model.name, :dependent => :destroy
24
+ accepts_nested_attributes_for :translations
25
+
26
+ named_scope :with_translations, :include => :translations
27
+ named_scope :with_current_translation, lambda {
28
+ {
29
+ :include => :translations,
30
+ :conditions => ["#{translation_model.table_name}.locale = ? OR #{translation_model.table_name}.locale = ?", I18n.locale.to_s, I18n.default_locale.to_s]
31
+ }
32
+ }
33
+ named_scope :with_translation_for, lambda { |locale|
34
+ {
35
+ :inlude => :translations,
36
+ :conditions => ["#{translation_model.table_name}.locale = ? OR #{translation_model.table_name}.locale = ?", locale.to_s, I18n.default_locale.to_s]
37
+ }
38
+ }
39
+
40
+ after_save :update_translations!
41
+
42
+ self.send :include, InstanceMethods
43
+ end
44
+
45
+ def define_translation_accessors field
46
+ field = field.to_sym
47
+ class_eval do
48
+ define_method "#{field}_translation" do |locale|
49
+ locale = locale.to_sym
50
+ # search in cache
51
+ if efficient_translations_attributes[locale][field]
52
+ efficient_translations_attributes[locale][field]
53
+ else
54
+ # search in relationship
55
+ translation = translations.detect { |t| t.locale.to_sym == locale }
56
+ if translation
57
+ translation[field]
58
+ elsif locale != I18n.default_locale
59
+ # try to fetch default locale
60
+ self.send "#{field}_translation", I18n.default_locale
61
+ end
62
+ end
63
+ end
64
+
65
+ define_method "set_#{field}_translation" do |locale, value|
66
+ locale = locale.to_sym
67
+ efficient_translations_attributes[locale][field] = value
68
+ end
69
+
70
+ define_method field do
71
+ self.send "#{field}_translation", I18n.locale
72
+ end
73
+
74
+ define_method "#{field}=" do |value|
75
+ self.send "set_#{field}_translation", I18n.locale, value
76
+ end
77
+
78
+ define_method("#{field}_translations") do
79
+ found = {}
80
+ efficient_translations_attributes.each do |locale, translation|
81
+ found[locale] = translation[field]
82
+ end
83
+ translations.inject(found) do |memo, translation|
84
+ memo[translation.locale.to_sym] ||= translation[field]
85
+ memo
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ module InstanceMethods
93
+
94
+ private
95
+
96
+ # attributes are stored in @efficient_attributes instance variable via setter
97
+ def efficient_translations_attributes
98
+ @efficient_translations_attributes ||= Hash.new { |hash, key| hash[key] = {} }
99
+ end
100
+
101
+ def update_translations!
102
+ if efficient_translations_attributes.present?
103
+ translations true #force reload all translations
104
+ efficient_translations_attributes.each do |locale, attributes|
105
+ translation = translations.detect { |t| t.locale.to_sym == locale } || begin
106
+ args = { :locale => locale }
107
+ args[self.class.translation_model.translatable_relation_field] = self
108
+ self.class.translation_model.new args
109
+ end
110
+ translation.update_attributes! attributes
111
+ end
112
+ end
113
+ end
114
+
115
+ def default_locale_required
116
+ unless translations.detect { |t| t.locale.to_sym == I18n.default_locale }
117
+ errors.add :translations, "for #{I18n.default_locale} is missing"
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,31 @@
1
+ module EfficientTranslations
2
+ module TranslationFactory
3
+ def self.new_model base_model
4
+ if base_model.const_defined?(:Translation)
5
+ base_model.const_get(:Translation)
6
+ else
7
+ klass = base_model.const_set(:Translation, Class.new(::ActiveRecord::Base))
8
+ klass.instance_eval do
9
+ cattr_accessor :translatable_model, :translatable_relation_field
10
+ self.translatable_model = base_model
11
+ self.translatable_relation_field = base_model.name.underscore.gsub '/', '_'
12
+
13
+ table_name = "#{base_model.table_name.singularize}_translations"
14
+ belongs_to translatable_relation_field
15
+
16
+ named_scope :for_locale, lambda { |locale|
17
+ { :conditions => ['locale = ? OR locale = ?', locale.to_s, I18n.locale.to_s] }
18
+ }
19
+
20
+ before_save :stringify_locale!
21
+ end
22
+ klass.class_eval do
23
+ def stringify_locale!
24
+ self.locale = locale.to_s
25
+ end
26
+ end
27
+ klass
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module EfficientTranslations
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table 'my_models', :force => true do |t|
3
+ end
4
+
5
+ create_table 'my_model_translations', :force => true do |t|
6
+ t.integer :my_model_id, :null => false
7
+ t.string :locale, :null => false
8
+ t.string :name
9
+ end
10
+
11
+ create_table 'working_models', :force => true do |t|
12
+ end
13
+
14
+ create_table 'working_model_translations', :force => true do |t|
15
+ t.integer :working_model_id, :null => false
16
+ t.string :locale, :null => false
17
+ t.string :name
18
+ t.string :content
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe EfficientTranslations::Schema do
4
+ let(:schema) { EfficientTranslations::FakeSchemaAdapter.new }
5
+
6
+ describe '#create_translation_table' do
7
+ it 'should create a table named <model>_translations' do
8
+ schema.should_receive(:create_table).with('pippo_translations')
9
+ schema.create_translation_table 'pippo', :name => :string
10
+ end
11
+
12
+ it 'should create the given translation columns' do
13
+ schema.should_receive(:add_column).with('pippo_translations', 'name', :string)
14
+ schema.create_translation_table 'pippo', :name => :string
15
+ end
16
+
17
+ it 'should create indexes' do
18
+ schema.should_receive(:add_index).twice
19
+ schema.create_translation_table 'pippo', :name => :string
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+
4
+ describe EfficientTranslations do
5
+ def my_model_class
6
+ Kernel.silence_warnings do
7
+ Kernel.const_set :MyModel, Class.new(ActiveRecord::Base)
8
+ end
9
+ end
10
+
11
+ describe '::translates' do
12
+ it 'should be defined in ActiveRecord::Base' do
13
+ ActiveRecord::Base.should respond_to :translates
14
+ end
15
+
16
+ it 'could be invoked specifing multiple attributes' do
17
+ model = my_model_class
18
+ lambda { model.translates :name, :content }.should_not raise_error
19
+ end
20
+
21
+ it 'could be invoked multiple times' do
22
+ model = my_model_class
23
+ lambda { model.translates :name }.should_not raise_error
24
+ lambda { model.translates :content }.should_not raise_error
25
+ end
26
+
27
+ it 'should generate the translation model the first time it\'s invoked' do
28
+ model = my_model_class
29
+ model.should_receive :make_efficient_translatable!
30
+ model.translates :name
31
+ end
32
+
33
+ it 'should not regenerate the translation model when called multiple times' do
34
+ model = my_model_class
35
+ model.translates :name
36
+ model.should_not_receive :make_efficient_translatable!
37
+ model.translates :content
38
+ end
39
+ end
40
+
41
+ describe '::validates_presence_of_default_locale' do
42
+ it 'should be defined in ActiveRecord::Base' do
43
+ ActiveRecord::Base.should respond_to :validates_presence_of_default_locale
44
+ end
45
+
46
+ it 'should prevent saving a model without default locale' do
47
+ model = my_model_class
48
+ model.translates :name
49
+ model.validates_presence_of_default_locale
50
+ lambda { model.new.save! }.should raise_error ActiveRecord::RecordInvalid
51
+ inst = model.new :translations_attributes => [{ :locale => :en, :name => 'pippo' }]
52
+ lambda { inst.save! }.should_not raise_error
53
+ end
54
+ end
55
+
56
+ context '::WorkingModel' do
57
+ it 'should include the translation model' do
58
+ WorkingModel.translation_model.should be_kind_of(Class)
59
+ end
60
+
61
+ it 'should include the translations relationship' do
62
+ WorkingModel.new.should respond_to :translations
63
+ end
64
+
65
+ it 'should have some utility named scopes' do
66
+ WorkingModel.should respond_to :with_translations
67
+ WorkingModel.should respond_to :with_current_translation
68
+ WorkingModel.should respond_to :with_translation_for
69
+ end
70
+
71
+ it 'should accept nested attributes' do
72
+ WorkingModel.delete_all
73
+ WorkingModel.create! :translations_attributes => [{ :locale => :en, :name => 'pippo' }]
74
+ WorkingModel.first.name.should == 'pippo'
75
+ end
76
+
77
+ describe 'field_translation' do
78
+ before do
79
+ @model = WorkingModel.new
80
+ @model.set_name_translation :en, 'pippo'
81
+ @model.save!
82
+ end
83
+
84
+ context 'when cache contains the translated value' do
85
+ before do
86
+ @model.set_name_translation :en, 'foo'
87
+ end
88
+
89
+ it 'should fetch the value from cache' do
90
+ @model.name_translation(:en).should == 'foo'
91
+ end
92
+ end
93
+
94
+ context 'when cache is empty' do
95
+ it 'should search in the relationship' do
96
+ @model = WorkingModel.find @model.id
97
+ @model.translation_model.create! :working_model_id => @model.id, :locale => 'fr', :name => 'frfr'
98
+ @model.name_translation(:fr).should == 'frfr'
99
+ end
100
+ end
101
+
102
+ context 'when cache is empty and no value is found' do
103
+ it 'should search for I18n.default_locale if locale != I18n.default_locale' do
104
+ I18n.default_locale = :en
105
+ @model.name_translation(:de).should == @model.name_translation(:en)
106
+ end
107
+
108
+ it 'should return nil if locale == I18n.default_locale' do
109
+ I18n.default_locale = :de
110
+ @model.name_translation(:de).should be_nil
111
+ end
112
+ end
113
+ end
114
+
115
+ describe 'set_field_translation' do
116
+ it 'should only set values in a cache, until save' do
117
+ model = WorkingModel.create!
118
+ model.set_name_translation :en, 'pippo'
119
+ model.translation_model.find_all_by_working_model_id(model.id).should be_empty
120
+ model.save!
121
+ model.translation_model.find_all_by_working_model_id(model.id).should_not be_empty
122
+ end
123
+ end
124
+
125
+ describe 'field accessor' do
126
+ it '<field> should be a wrapper for <field>_translation' do
127
+ model = WorkingModel.new
128
+ model.should_receive(:name_translation).with(:en)
129
+ I18n.locale = :en
130
+ model.name
131
+ end
132
+
133
+ it '<field>= should be a wrapper for set_<field>_translation' do
134
+ model = WorkingModel.new
135
+ model.should_receive(:set_name_translation).with(:en, 'pippo')
136
+ I18n.locale = :en
137
+ model.name = 'pippo'
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe EfficientTranslations::TranslationFactory do
4
+ before :each do
5
+ Kernel.silence_warnings do
6
+ @model_class = Kernel.const_set :MyModel, Class.new(ActiveRecord::Base)
7
+ end
8
+ end
9
+
10
+ describe '::new_model' do
11
+ context 'when no translation model is found' do
12
+ before do
13
+ @klass = Class.new(ActiveRecord::Base)
14
+ @model_class.stub :const_set => @klass
15
+ end
16
+
17
+ it 'should create a new ::Translation class' do
18
+ @model_class.should_receive(:const_set).with(:Translation, kind_of(Class)) { |name, klass| klass }
19
+ EfficientTranslations::TranslationFactory::new_model @model_class
20
+ end
21
+
22
+ it 'should define a belongs_to association to the main model' do
23
+ @klass.should_receive(:belongs_to)
24
+ EfficientTranslations::TranslationFactory::new_model @model_class
25
+ end
26
+
27
+ it 'should assign the translatable model in an accessor' do
28
+ translation = EfficientTranslations::TranslationFactory::new_model @model_class
29
+ translation.translatable_model.should == @model_class
30
+ end
31
+
32
+ it 'should assign the translatable model field in an accessor' do
33
+ translation = EfficientTranslations::TranslationFactory::new_model @model_class
34
+ translation.translatable_relation_field.should == @model_class.name.underscore.gsub('/','_')
35
+ end
36
+
37
+ it 'should return the created translation class' do
38
+ klass = Class.new(ActiveRecord::Base)
39
+ @model_class.stub :const_set => klass
40
+ EfficientTranslations::TranslationFactory::new_model(@model_class).should == klass
41
+ end
42
+ end
43
+
44
+ context 'when a translation model is found' do
45
+ it 'should not create a ::Translation class' do
46
+ EfficientTranslations::TranslationFactory::new_model @model_class
47
+ @model_class.should_not_receive(:const_set)
48
+ EfficientTranslations::TranslationFactory::new_model @model_class
49
+ end
50
+
51
+ it 'should return the already defined translation class' do
52
+ klass = Class.new(ActiveRecord::Base)
53
+ @model_class.stub :const_set => klass
54
+ EfficientTranslations::TranslationFactory::new_model @model_class
55
+ EfficientTranslations::TranslationFactory::new_model(@model_class).should == klass
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ # Load the Sinatra app
2
+ require File.dirname(__FILE__) + '/../lib/efficient_translations'
3
+
4
+ require 'rspec'
5
+
6
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
7
+
8
+ RSpec.configure do |conf|
9
+ conf.before :suite do
10
+ ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
11
+ require File.join(File.dirname(__FILE__), 'fixtures', 'schema.rb')
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module EfficientTranslations
2
+ class FakeSchemaAdapter
3
+ include Schema
4
+
5
+ #stubs methods
6
+
7
+ def create_table table
8
+ end
9
+
10
+ def add_column table, column_name, type
11
+ end
12
+
13
+ def add_index table, columns, args = {}
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ class WorkingModel < ActiveRecord::Base
2
+ translates :name, :content
3
+ end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: efficient_translations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nicola Racco
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-08 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &70105172048080 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70105172048080
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70105172047020 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70105172047020
36
+ - !ruby/object:Gem::Dependency
37
+ name: sqlite3
38
+ requirement: &70105172046400 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70105172046400
47
+ - !ruby/object:Gem::Dependency
48
+ name: activerecord
49
+ requirement: &70105172045360 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.3'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70105172045360
58
+ - !ruby/object:Gem::Dependency
59
+ name: activesupport
60
+ requirement: &70105172043440 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '2.3'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70105172043440
69
+ description: Translation library for ActiveRecord models in Rails 2
70
+ email:
71
+ - nicola@nicolaracco.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - README.md
80
+ - Rakefile
81
+ - efficient_translations.gemspec
82
+ - lib/efficient_translations.rb
83
+ - lib/efficient_translations/schema.rb
84
+ - lib/efficient_translations/translates_method.rb
85
+ - lib/efficient_translations/translation_factory.rb
86
+ - lib/efficient_translations/version.rb
87
+ - spec/fixtures/schema.rb
88
+ - spec/lib/efficient_translations/schema_spec.rb
89
+ - spec/lib/efficient_translations/translates_method_spec.rb
90
+ - spec/lib/efficient_translations/translation_factory_spec.rb
91
+ - spec/spec_helper.rb
92
+ - spec/support/fake_schema_adapter.rb
93
+ - spec/support/working_model.rb
94
+ homepage: ''
95
+ licenses: []
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ segments:
107
+ - 0
108
+ hash: 4009622527868518176
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ segments:
116
+ - 0
117
+ hash: 4009622527868518176
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.10
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Translation library for ActiveRecord models in Rails 2 with an eye on performances
124
+ test_files:
125
+ - spec/fixtures/schema.rb
126
+ - spec/lib/efficient_translations/schema_spec.rb
127
+ - spec/lib/efficient_translations/translates_method_spec.rb
128
+ - spec/lib/efficient_translations/translation_factory_spec.rb
129
+ - spec/spec_helper.rb
130
+ - spec/support/fake_schema_adapter.rb
131
+ - spec/support/working_model.rb