translate_columns 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+
2
+ source :rubygems
3
+ gemspec
4
+
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ translate_columns (1.1.0)
5
+ activerecord (~> 3.0.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.0.3)
11
+ activesupport (= 3.0.3)
12
+ builder (~> 2.1.2)
13
+ i18n (~> 0.4)
14
+ activerecord (3.0.3)
15
+ activemodel (= 3.0.3)
16
+ activesupport (= 3.0.3)
17
+ arel (~> 2.0.2)
18
+ tzinfo (~> 0.3.23)
19
+ activesupport (3.0.3)
20
+ arel (2.0.7)
21
+ builder (2.1.2)
22
+ i18n (0.5.0)
23
+ mocha (0.9.10)
24
+ rake
25
+ rake (0.8.7)
26
+ sqlite3 (1.3.3)
27
+ tzinfo (0.3.24)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ activerecord (~> 3.0.0)
34
+ mocha (~> 0.9.3)
35
+ sqlite3 (~> 1.3.3)
36
+ translate_columns!
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ Copyright (c) 2007 Samuel Lown
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4
+ and associated documentation files (the “Software”), to deal in the Software without restriction,
5
+ including without limitation the rights to use, copy, modify, merge, publish, distribute,
6
+ sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
7
+ is furnished to do so, subject to the following conditions:
8
+
9
+ The above copyright notice and this permission notice shall be included in all copies or
10
+ substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
13
+ BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
15
+ OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,262 @@
1
+ = Translate Columns Plugin
2
+
3
+ Copyright (c) 2007-2011 Samuel Lown <me (AT) samlown.com>
4
+
5
+ This Plugin is released under the MIT license, as Rails itself. Please see the
6
+ attached LICENSE file for further details.
7
+
8
+ This document and plugin should be considered a work in progress until further
9
+ notice!
10
+
11
+ == Introduction
12
+
13
+ The aim of the Translate Columns plugin is to aid the normally difficult task
14
+ of supporting multiple languages in the models. It provides a near transparent
15
+ interface to the data contained in the models and their translations such that
16
+ your current controllers, views and models only need to be modified slightly
17
+ to support multiple languages in a scalable fashion.
18
+
19
+ If you already have your rails app set up and functioning, using translate
20
+ columns will not require any major refactoring of your code (unless you're
21
+ really unlucky), and can be simply added. Indeed, the plugin was written to be
22
+ added to an existing application.
23
+
24
+ == Updates
25
+
26
+ === v1.1 - 21st January 2011
27
+
28
+ - Now only supports ActiveRecord 3
29
+ - Converted to gem
30
+ - +include TranslateColumns+ now required on a per model basis
31
+
32
+ === 24th September 2009
33
+
34
+ - Added support for setting the locale variable on the parent, which disables translations. See below.
35
+
36
+ === 23rd September 2009
37
+
38
+ - Testing finally added (hope to add more tests soon)
39
+ - Validations now only performed by the parent model
40
+ - Changed namespace to TranslateColumns (as opposed to Translate::Columns)
41
+ - Parent model's +locale+ variable changed to +translation_locale+ so that it can be added as a column/attribute if needed.
42
+
43
+ === 20th May 2009
44
+
45
+ - Finally got round to moving to github
46
+ - Added support for the rails 2.2 I18n stuff (WIN!)
47
+
48
+ *WARNING* Translate Columns will now only work with Rails 3 as a gem. For older
49
+ 2.3 projects, use as a plugin with the v_1.0 release tag.
50
+
51
+ == Architecture
52
+
53
+ Translate columns while simple, does require a specific architecture. The basic
54
+ idea is that each of your models has an associated model that defines the
55
+ translations. An ASCII ERM that uses an example primary class called document
56
+ follows:
57
+
58
+ ____________ _______________________
59
+ | | 1 * | |
60
+ | Document |---------------| DocumentTranslation |
61
+ |____________| |_______________________|
62
+
63
+
64
+ The data contained by these entities may be similar to the following:
65
+
66
+ Document:
67
+
68
+ | Column | Type |
69
+ -------------------------
70
+ | id | integer |
71
+ | name | string |
72
+ | title | string |
73
+ | sub_title | string |
74
+ | body | text |
75
+ | created_on | datetime |
76
+ | updated_on | datetime |
77
+
78
+ DocumentTranslation:
79
+
80
+ | Column | Type |
81
+ --------------------------
82
+ | id | integer |
83
+ | document_id | integer |
84
+ | locale | string |
85
+ | title | string |
86
+ | sub_title | string |
87
+ | body | text |
88
+
89
+ In Rails, thsee models would be defined as follows:
90
+
91
+ class Document < ActiveRecord::Base
92
+ has_many :translations, :class_name => 'DocumentTranslation'
93
+ end
94
+
95
+ class DocumentTranslation < ActiveRecord::Base
96
+ belongs_to :document
97
+ end
98
+
99
+ Each DocumentTranslation belongs to a Document and defines the locale of the
100
+ translation and only those fields that require a translation. If you really
101
+ wanted to, a composite key could be used on the document_id and the locale,
102
+ as these should always uniquely identify the translation.
103
+
104
+ In previous versions of translate_columns a Locale model and associations was
105
+ used to determine the language of a translation, this is no longer required
106
+ with the new Rails 2.2 I18n code and simple string for the locale code
107
+ of your choice can be used instead.
108
+
109
+ IMPORTANT: Default locale. In order for this setup to work, there must be a
110
+ single, pre-defined locale for the default data, this is the data contained
111
+ in the 'Document' entity and will be used whenever we're operating in default
112
+ mode, or if there is no translation available. It is essential that this
113
+ default locale *never* change during the lifetime of your application,
114
+ otherwise you'll end up with a mess.
115
+
116
+ The Document's translations association uses the :class_name option to name the
117
+ correct class. Aside from saving on typing, this is an essential requirement
118
+ of the translate_columns plugin. (At least until I get chance to add an option
119
+ to allow for different names.)
120
+
121
+ == Installation
122
+
123
+ Assuming you've read the above and understand the basic requirements, the
124
+ plugin can now be installed and setup.
125
+
126
+ The latest details and updates are available on the github repository:
127
+
128
+ http://github.com/samlown/translate_columns
129
+
130
+ To install plugin, use the standard rails plugin install method:
131
+
132
+ ./script/plugin install git://github.com/samlown/translate_columns.git
133
+
134
+ There are no more installation steps, and the plugin does not install any extra
135
+ files or customise the setup. To uninstall, simply remove the directory.
136
+
137
+ == Setup
138
+
139
+ Now for the hard part :-) Re-using the example above for documents, to use the
140
+ plugin modify the model so that it looks like the following:
141
+
142
+ class Document < ActiveRecord::Base
143
+ include TranslateColumns
144
+ has_many :translations, :class_name => 'DocumentTranslation'
145
+ translate_columns :title, :sub_title, :body
146
+ end
147
+
148
+ I'm working on getting it so that you don't need to specify the columns
149
+ manually, but it is not yet ready.
150
+
151
+ In earlier versions you'd need to mess around with a Locale class but thanks to
152
+ the Rails I18n extension, this is no longer necessary.
153
+
154
+ == Upgrading
155
+
156
+ If you're using a realy old version of Translate Columns, then you'll need to perform an
157
+ upgrade and migration to use the fabulous new I18n Rails code. Fortunately, this is
158
+ very easy to do.
159
+
160
+ To upgrade, remove and previous entries to your Locale class in you translation models
161
+ and generate a migration to convert the local_id column into a string. Something like
162
+ the following will surfice.
163
+
164
+ class UpgradeTranslationModels < ActiveRecord::Migration
165
+ def self.up
166
+ alter_column :product_translations, :locale_id, :string, :length => 10
167
+ rename_column :product_translations, :locale_id, :locale
168
+ end
169
+
170
+ def self.down
171
+ rename_column :product_translations, :locale, :locale_id
172
+ alter_column :product_translations, :locale_id, :integer
173
+ end
174
+ end
175
+
176
+ After ensuring you're using the I18n.locale calls throughout your application, it
177
+ should work fine.
178
+
179
+ == Usage
180
+
181
+ The idea here is that you forget about the fact your models can be translated
182
+ and just use the app as normal. Indeed, if you don't set a locale, you
183
+ won't even notice the plugin is there.
184
+
185
+ Here's a really basic example of what we can do on the console.
186
+
187
+ >> I18n.locale = I18n.default_locale # First try default language
188
+ => :en
189
+ >> doc = Document.find(:first)
190
+ -- output hidden --
191
+ >> doc.title
192
+ => "Sample Document" # title in english
193
+ >> I18n.locale = 'es' # set to other language
194
+ => "es"
195
+ >> doc = Document.find(:first) # Reload to avoid caching problems!
196
+ -- output hidden --
197
+ >> doc.title
198
+ => "Titulo español" # Title now in spanish
199
+ >> doc.title_default
200
+ => "Sample Document" # original field data
201
+ >> doc.title = "Nuevo Título Español"
202
+ => "Nuevo Título Español"
203
+ >> doc.save # set the title and save
204
+ => true
205
+ >> I18n.locale = 'en'
206
+ => "en" # return to english
207
+ >> doc = Document.find(:first)
208
+ -- output hidden --
209
+ >> doc.title
210
+ => "Sample Document"
211
+
212
+ As can be seen, just by setting the locale we are able to edit the data
213
+ without having to worry about the details.
214
+
215
+ The current version also has support for disabling translations by giving the
216
+ parent object a +locale+ field and setting it to something. This is actually a
217
+ very powerful feature as it allows new objects to be created under a specific locale
218
+ and filtered as such. A typical example would be a blog where most of the posts
219
+ you'd like to be translated into several languages, but occaisionly some posts will only
220
+ be relevant for a specific region:
221
+
222
+ >> I18n.locale = I18n.default_locale
223
+ >> post = Post.new(:title => "Example") # WIN
224
+ >>
225
+ >> I18n.locale = 'es'
226
+ >> post = Post.new(:title => "Ejemplo") # FAIL
227
+ TranslateColumns::MissingParent: Cannot create translations without a stored parent
228
+ >>
229
+ >> post = Post.new(:locale => 'es', :title => "Ejemplo") # WIN
230
+ >>
231
+ >> # Provide posts, with either a translation of for the current locale
232
+ >> posts = Post.paginate(:conditions => ['posts.locale IS NULL OR posts.locale = ?', I18n.locale.to_s])
233
+
234
+ A useful +named_scope+ could be as follows:
235
+
236
+ class Post < ActiveRecord::Base
237
+ # ... translate columns stuff ...
238
+ named_scope :for_current_locale, :conditions => ['posts.locale IS NULL OR posts.locale = ?', I18n.locale.to_s]
239
+ end
240
+
241
+ @posts = Post.for_current_locale.paginate
242
+
243
+ Changing locale of an object after it has been created will cause its translations to be ignored, but
244
+ by emptying the locale value the translations should work as before.
245
+ Of course, if you don't want this funcionality simply do not add a locale attribute or method to
246
+ the parent model.
247
+
248
+
249
+ == How it works
250
+
251
+ The plugin overrides the default attribute accessor functions and automatically
252
+ uses the 'translations' association to find the request fields. It also
253
+ provides a new method that extends the original method name to access
254
+ the original values.
255
+
256
+ == Todos / Bugs
257
+
258
+ * Caching - Using a basic rails setup, everything should work fine, however
259
+ if you have a more complex caching setup strange things might happen.
260
+ Please mail me if you have any problems!
261
+
262
+
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the translate_columns plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test/lib'
12
+ t.pattern = 'test/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the translate_columns plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'TranslateColumns'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+
2
+ require 'translate_columns'
3
+
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,228 @@
1
+ # encoding: utf-8
2
+ #
3
+ # TranslateColumns
4
+ #
5
+ # Copyright (c)2007-2011 Samuel Lown <me@samlown.com>
6
+ #
7
+ module TranslateColumns
8
+
9
+ class MissingParent < StandardError
10
+ end
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ # methods used in the class definition
17
+ module ClassMethods
18
+
19
+ # Read the provided list of symbols as column names and
20
+ # generate methods for each to access translated versions.
21
+ #
22
+ # Possible options, after the columns, include:
23
+ #
24
+ # * :locale_field - Name of the field in the parents translation table
25
+ # of the locale. This defaults to 'locale'.
26
+ #
27
+ def translate_columns( *options )
28
+
29
+ locale_field = 'locale'
30
+
31
+ columns = [ ]
32
+ if ! options.is_a? Array
33
+ raise "Provided parameter to translate_columns is not an array!"
34
+ end
35
+ # extract all the options
36
+ options.each do | opt |
37
+ if opt.is_a? Symbol
38
+ columns << opt
39
+ elsif opt.is_a? Hash
40
+ # Override the locale class if set.
41
+ locale_field = opt[:locale_field]
42
+ end
43
+ end
44
+
45
+ define_method 'columns_to_translate' do
46
+ columns.collect{ |c| c.to_s }
47
+ end
48
+
49
+ # set the instance Methods first
50
+ include TranslateColumns::InstanceMethods
51
+
52
+ # Rails magic to override the normal save process
53
+ alias_method_chain :save, :translation
54
+ alias_method_chain :save!, :translation
55
+ alias_method_chain :attributes=, :locale
56
+
57
+ # Generate a module containing methods that override access
58
+ # to the ActiveRecord methods.
59
+ # This dynamic module is then included in the parent such that
60
+ # the super method will function correctly.
61
+ mod = Module.new do | m |
62
+
63
+ columns.each do | column |
64
+
65
+ next if ['id', locale_field].include?(column.to_s)
66
+
67
+ # This is strange, so allow me to explain:
68
+ # We define access to the original method and its super,
69
+ # a normal "alias" can't find the super which is the method
70
+ # created by ActionBase.
71
+ # The Alias_method function takes a copy, and retains the
72
+ # ability to call the parent with the same name.
73
+ # Finally, the method is overwritten to support translation.
74
+ #
75
+ # All this is to avoid defining parameters for the overwritten
76
+ # accessor which normally doesn't have them.
77
+ # (Warnings are produced on execution when a metaprogrammed
78
+ # function is called without parameters and its expecting them)
79
+ #
80
+ # Sam Lown (2007-01-17) dev at samlown dot com
81
+ define_method(column) do
82
+ # This super should call the missing_method method in ActiveRecord.
83
+ super()
84
+ end
85
+
86
+ alias_method("#{column}_before_translation", column)
87
+
88
+ # overwrite accessor to read
89
+ define_method("#{column}") do
90
+ if translation and ! translation.send(column).blank?
91
+ translation.send(column)
92
+ else
93
+ super()
94
+ end
95
+ end
96
+
97
+ define_method("#{column}_before_type_cast") do
98
+ if (translation)
99
+ translation.send("#{column}_before_type_cast")
100
+ else
101
+ super
102
+ end
103
+ end
104
+
105
+ define_method("#{column}=") do |value|
106
+ # translation object must have already been set up for this to work!
107
+ if (translation)
108
+ translation.send("#{column}=",value)
109
+ else
110
+ super( value )
111
+ end
112
+ end
113
+
114
+ end
115
+ end # dynamic module
116
+
117
+ # include the anonymous module so that the "super" method
118
+ # will work correctly in the child!
119
+ include mod
120
+ end
121
+
122
+ end
123
+
124
+ # Methods that are specific to the current class
125
+ # and only called when translate_columns is used
126
+ module InstanceMethods
127
+
128
+ # Provide the locale which is currently in use with the object or the current global locale.
129
+ # If the default is in use, always return nil.
130
+ def translation_locale
131
+ locale = @translation_locale || I18n.locale.to_s
132
+ locale == I18n.default_locale.to_s ? nil : locale
133
+ end
134
+
135
+ # Setting the locale will always enable translation.
136
+ # If set to nil the global locale is used.
137
+ def translation_locale=(locale)
138
+ enable_translation
139
+ # TODO some checks for available translations would be nice.
140
+ # I18n.available_locales only available as standard with rails 2.3
141
+ @translation_locale = locale.to_s.empty? ? nil : locale.to_s
142
+ end
143
+
144
+ # Do not allow translations!
145
+ def disable_translation
146
+ @disable_translation = true
147
+ end
148
+ def enable_translation
149
+ @disable_translation = false
150
+ end
151
+
152
+ # Important check to see if the parent has a locale method.
153
+ # If so, translations should be disabled if it is set to something!
154
+ def has_locale_value?
155
+ respond_to?(:locale) && !self.locale.to_s.empty?
156
+ end
157
+
158
+ # determine if the conditions are set for a translation to be used
159
+ def translation_enabled?
160
+ (!@disable_translation && translation_locale) and !has_locale_value?
161
+ end
162
+
163
+ # Provide a translation object based on the parent and the translation_locale
164
+ # current value.
165
+ def translation
166
+ if translation_enabled?
167
+ if !@translation || (@translation.locale != translation_locale)
168
+ raise MissingParent, "Cannot create translations without a stored parent" if new_record?
169
+ # try to find translation or build a new one
170
+ @translation = translations.find_by_locale(translation_locale) || translations.build(:locale => translation_locale)
171
+ end
172
+ @translation
173
+ else
174
+ nil
175
+ end
176
+ end
177
+
178
+ # As this is included in a mixin, a "super" call from inside the
179
+ # child (inheriting) class will infact look here before looking to
180
+ # ActiveRecord for the real 'save'. This method should therefore
181
+ # be safely overridden if needed.
182
+ #
183
+ # Assumes validation enabled in ActiveRecord and performs validation
184
+ # before saving. This means the base records validation checks will always
185
+ # be used.
186
+ #
187
+ def save_with_translation(*args)
188
+ perform_validation = args.is_a?(Hash) ? args[:validate] : args
189
+ if perform_validation && valid? || !perform_validation
190
+ translation.save(*args) if (translation)
191
+ disable_translation
192
+ save_without_translation(*args)
193
+ enable_translation
194
+ true
195
+ else
196
+ false
197
+ end
198
+ end
199
+
200
+ def save_with_translation!
201
+ if valid?
202
+ translation.save! if (translation)
203
+ disable_translation
204
+ save_without_translation!
205
+ enable_translation
206
+ else
207
+ raise ActiveRecord::RecordInvalid.new(self)
208
+ end
209
+ rescue
210
+ enable_translation
211
+ raise
212
+ end
213
+
214
+ # Override the default mass assignment method so that the locale variable is always
215
+ # given preference.
216
+ def attributes_with_locale=(new_attributes, guard_protected_attributes = true)
217
+ return if new_attributes.nil?
218
+ attributes = new_attributes.dup
219
+ attributes.stringify_keys!
220
+
221
+ attributes = sanitize_for_mass_assignment(attributes) if guard_protected_attributes
222
+ send(:locale=, attributes["locale"]) if attributes.has_key?("locale") and respond_to?(:locale=)
223
+
224
+ send(:attributes_without_locale=, attributes, guard_protected_attributes)
225
+ end
226
+
227
+ end
228
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :translate_columns do
3
+ # # Task goes here
4
+ # end
data/test/database.yml ADDED
@@ -0,0 +1,4 @@
1
+ sqlite3:
2
+ database: ":memory:"
3
+ adapter: sqlite3
4
+ timeout: 500
@@ -0,0 +1,11 @@
1
+ class Document < ActiveRecord::Base
2
+ include TranslateColumns
3
+
4
+ has_many :translations, :class_name => 'DocumentTranslation'
5
+ translate_columns :title, :body
6
+
7
+ validates_presence_of :title
8
+ validates_length_of :title, :within => 3..200
9
+
10
+ validates_length_of :body, :within => 3..500
11
+ end
@@ -0,0 +1,3 @@
1
+ class DocumentTranslation < ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,14 @@
1
+ translation1:
2
+ id: 1
3
+ document_id: 1
4
+ locale: es
5
+ title: Este es el titulo de un documento en Español
6
+ body: Nada
7
+
8
+ translation2:
9
+ id: 2
10
+ document_id: 1
11
+ locale: fr
12
+ title: Un title en francais
13
+ body:
14
+
@@ -0,0 +1,13 @@
1
+ document1:
2
+ id: 1
3
+ title: Test Document Number 1
4
+ body: This is a test document with some kind of body.
5
+ published_at: "2009-09-23 21:53:04"
6
+
7
+ document2:
8
+ id: 2
9
+ title: Test Document Number 2
10
+ locale: en
11
+ body: This is a second test document with some random content for the body.
12
+ published_at: "2009-09-23 21:53:46"
13
+
@@ -0,0 +1,18 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table "documents", :force => true do |t|
3
+ t.column "locale", :string, :length => 8
4
+ t.column "title", :string
5
+ t.column "body", :text
6
+ t.column "published_at", :datetime
7
+ t.timestamps
8
+ end
9
+
10
+ create_table "document_translations", :force => true do |t|
11
+ t.references "document"
12
+ t.string :locale
13
+ t.column "title", :string
14
+ t.column "body", :text
15
+ t.timestamps
16
+ end
17
+ end
18
+
@@ -0,0 +1,6 @@
1
+ require 'active_record'
2
+ require 'active_record/fixtures'
3
+
4
+ conf = YAML::load(File.open(File.dirname(__FILE__) + '/../database.yml'))
5
+ ActiveRecord::Base.establish_connection(conf['sqlite3'])
6
+
@@ -0,0 +1,10 @@
1
+
2
+ require 'activerecord_connector'
3
+ require File.join(File.dirname(__FILE__), '../fixtures/schema.rb')
4
+
5
+ module ActiverecordTestHelper
6
+ FIXTURES_PATH = File.join(File.dirname(__FILE__), '/../fixtures')
7
+ dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
8
+ dep.autoload_paths.unshift FIXTURES_PATH
9
+ end
10
+
@@ -0,0 +1,153 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler/setup'
4
+ require 'test/unit'
5
+ require 'mocha'
6
+
7
+ require 'activerecord_test_helper'
8
+ require 'translate_columns'
9
+
10
+ class TranslateColumnsTest < Test::Unit::TestCase
11
+
12
+ include ActiverecordTestHelper
13
+
14
+ def setup
15
+ @docs = Fixtures.create_fixtures(FIXTURES_PATH, ['documents', 'document_translations'])
16
+ end
17
+
18
+ def teardown
19
+ Fixtures.reset_cache
20
+ end
21
+
22
+ def test_basic_document_fields
23
+ doc = Document.find(:first)
24
+ assert_equal "Test Document Number 1", doc.title, "Document not found!"
25
+ assert_not_nil doc.body, "Empty document body"
26
+ assert_not_nil doc.published_at, "Missing published date"
27
+ end
28
+
29
+ def test_basic_document_fields_for_default_locale
30
+ I18n.locale = "en"
31
+ doc = Document.find(:first)
32
+ assert_equal "Test Document Number 1", doc.title, "Document not found!"
33
+ assert_not_nil doc.body, "Empty document body"
34
+ assert_not_nil doc.published_at, "Missing published date"
35
+ end
36
+
37
+ def test_count_translations
38
+ doc = Document.find(:first)
39
+ assert_equal 2, doc.translations.count, "Count doesn't match!"
40
+ end
41
+
42
+ def test_basic_document_fields_for_spanish
43
+ I18n.locale = "es"
44
+ doc = Document.find(:first)
45
+ assert_equal "Este es el titulo de un documento en Espa\303\261ol", doc.title, "Document not found!"
46
+ assert_equal "Nada", doc.body, "Different document body"
47
+ assert_not_nil doc.published_at, "Missing published date"
48
+ assert_equal "Test Document Number 1", doc.title_before_translation
49
+ end
50
+
51
+ def test_missing_fields_resort_to_original
52
+ I18n.locale = 'fr'
53
+ doc = Document.find(:first)
54
+ assert_equal "Un title en francais", doc.title
55
+ assert_match /body/, doc.body
56
+ assert doc.body_before_type_cast.to_s.empty?
57
+ end
58
+
59
+ def test_switching_languages_for_reading
60
+ I18n.locale = I18n.default_locale
61
+ doc1 = Document.find(:first)
62
+ assert_equal "Test Document Number 1", doc1.title
63
+ I18n.locale = 'es'
64
+ doc2 = Document.find(:first)
65
+ assert_equal doc1.title, doc2.title
66
+ assert_not_equal "Test Document Number 1", doc1.title
67
+ I18n.locale = 'en'
68
+ assert_equal doc1.title, doc2.title
69
+ assert_equal "Test Document Number 1", doc1.title
70
+ end
71
+
72
+ def test_setting_fields_in_default_language
73
+ time_now = Time.now
74
+ I18n.locale = I18n.default_locale
75
+ doc1 = Document.find(:first)
76
+ doc1.title = "A new title"
77
+ doc1.published_at = time_now
78
+ assert doc1.save, "Unable to save document"
79
+ assert_equal "A new title", doc1.title
80
+ # Now change language
81
+ I18n.locale = 'es'
82
+ doc1 = Document.find(:first)
83
+ assert_not_equal "A new title", doc1.title
84
+ assert_equal time_now.to_s, doc1.published_at.to_s
85
+ end
86
+
87
+ def test_saving_changes_in_translations
88
+ time_now = Time.now
89
+ I18n.locale = 'es'
90
+ doc1 = Document.find(:first)
91
+ doc1.title = "Un nuevo título"
92
+ doc1.published_at = time_now
93
+ assert doc1.save
94
+ I18n.locale = I18n.default_locale
95
+ doc1 = Document.find(:first)
96
+ assert_not_equal "Un nuevo título", doc1.title
97
+ assert_equal time_now.to_s, doc1.published_at.to_s
98
+ end
99
+
100
+ def test_creating_new_documents
101
+ I18n.locale = I18n.default_locale
102
+ doc = Document.new(:title => "A new document", :body => "The Body")
103
+ assert doc.save
104
+ end
105
+
106
+ def test_creating_new_documents_under_locale_fails
107
+ I18n.locale = 'es'
108
+ assert_raise TranslateColumns::MissingParent do
109
+ Document.new(:title => "Un nuevo documento", :body => 'El cuerpo')
110
+ end
111
+ end
112
+
113
+ def test_failed_validations
114
+ I18n.locale = I18n.default_locale
115
+ doc = Document.find(:first)
116
+ doc.title = "a"
117
+ assert !doc.save
118
+ assert !doc.errors[:title].empty?
119
+ end
120
+
121
+ def test_failed_validations_on_translation
122
+ I18n.locale = 'es'
123
+ doc = Document.find(:first)
124
+ doc.title = "a"
125
+ assert !doc.save
126
+ assert !doc.errors[:title].empty?
127
+ end
128
+
129
+ def test_locale_attribute_detection
130
+ doc = Document.find(:first)
131
+ assert !doc.has_locale_value?
132
+ doc.locale = "en"
133
+ assert doc.has_locale_value?
134
+ end
135
+
136
+ def test_locale_attribute_detection_without_attribute
137
+ doc = Document.find(:first)
138
+ doc.locale = "en"
139
+ doc.stubs(:respond_to?).with(:locale).returns(false)
140
+ assert !doc.has_locale_value?
141
+ end
142
+
143
+ def test_create_new_document_with_specific_locale
144
+ I18n.locale = 'es'
145
+ doc = nil
146
+ assert_nothing_thrown do
147
+ doc = Document.new(:locale => 'es', :title => "A new document", :body => "Test Body")
148
+ end
149
+ assert doc.locale, 'es'
150
+ assert doc.save
151
+ end
152
+
153
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{translate_columns}
5
+ s.version = File.read(File.join(File.dirname(__FILE__), 'VERSION')).strip
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.5") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Sam Lown"]
9
+ s.date = %q{2011-01-21}
10
+ s.description = %q{Automatically translate ActiveRecord columns using a second model containing the translations.}
11
+ s.email = %q{me@samlown.com}
12
+ s.extra_rdoc_files = [
13
+ "LICENSE",
14
+ "README.rdoc"
15
+ ]
16
+ s.homepage = %q{http://github.com/samlown/translate_columns}
17
+ s.rubygems_version = %q{1.3.7}
18
+ s.summary = %q{Use fields from other translation models easily}
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.require_paths = ["lib"]
23
+
24
+ s.add_dependency("activerecord", "~> 3.0.0")
25
+ s.add_development_dependency(%q<mocha>, "~> 0.9.3")
26
+ s.add_development_dependency(%q<sqlite3>, "~> 1.3.3")
27
+ end
28
+
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: translate_columns
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 1
8
+ - 0
9
+ version: 1.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Sam Lown
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-01-21 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: activerecord
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 3
30
+ - 0
31
+ - 0
32
+ version: 3.0.0
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: mocha
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 9
46
+ - 3
47
+ version: 0.9.3
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: sqlite3
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 1
60
+ - 3
61
+ - 3
62
+ version: 1.3.3
63
+ type: :development
64
+ version_requirements: *id003
65
+ description: Automatically translate ActiveRecord columns using a second model containing the translations.
66
+ email: me@samlown.com
67
+ executables: []
68
+
69
+ extensions: []
70
+
71
+ extra_rdoc_files:
72
+ - LICENSE
73
+ - README.rdoc
74
+ files:
75
+ - .gitignore
76
+ - Gemfile
77
+ - Gemfile.lock
78
+ - LICENSE
79
+ - README.rdoc
80
+ - Rakefile
81
+ - VERSION
82
+ - init.rb
83
+ - install.rb
84
+ - lib/translate_columns.rb
85
+ - tasks/translate_columns_tasks.rake
86
+ - test/database.yml
87
+ - test/fixtures/document.rb
88
+ - test/fixtures/document_translation.rb
89
+ - test/fixtures/document_translations.yml
90
+ - test/fixtures/documents.yml
91
+ - test/fixtures/schema.rb
92
+ - test/lib/activerecord_connector.rb
93
+ - test/lib/activerecord_test_helper.rb
94
+ - test/translate_columns_test.rb
95
+ - translate_columns.gemspec
96
+ has_rdoc: true
97
+ homepage: http://github.com/samlown/translate_columns
98
+ licenses: []
99
+
100
+ post_install_message:
101
+ rdoc_options: []
102
+
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">"
117
+ - !ruby/object:Gem::Version
118
+ segments:
119
+ - 1
120
+ - 3
121
+ - 5
122
+ version: 1.3.5
123
+ requirements: []
124
+
125
+ rubyforge_project:
126
+ rubygems_version: 1.3.7
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: Use fields from other translation models easily
130
+ test_files: []
131
+