translate_columns 1.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.
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
+