efficient_translations 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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