has_localization_table 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .project
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in has_localization_table.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Daniel Vandersluis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # HasLocalizationTable
2
+
3
+ ActiveRecord plugin which adds setup and convenience methods for working with a relational localization table for user-driven data.
4
+
5
+ Adds accessors to retrieve localized attributes using the current locale, in order to avoid having to collect the correct object each time a value is needed. Localized attribute values are also cached for the current locale.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'has_localization_table'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install has_localization_table
20
+
21
+ ## Usage
22
+
23
+ The gem assumes that the localization table has already been migrated, and the model for it contains `belongs_to` associations for the locale table and the base table. You only need to call the `has_localization_table` method on the base model.
24
+
25
+ class Article < ActiveRecord::Base
26
+ # assuming ArticleLocalizations has name and body columns
27
+ has_localization_table :localizations, required: true, class_name: "ArticleLocalizations"
28
+ end
29
+
30
+ # Localized attributes can be retrieved by accessor...
31
+ a = Article.new(name: "Once Upon a Time...", body: "There once lived a princess locked away in a tower!")
32
+ a.name # "Once Upon a Time..."
33
+ a.body # "There once lived a princess locked away in a tower!"
34
+
35
+ # ... or set directly
36
+ a.name = "Sleeping Beauty"
37
+
38
+ # After changing to a different locale
39
+ a.name = "Belle au Bois Dormant"
40
+ a.body = "Il était une fois une princesse enfermée dans une tour!"
41
+
42
+ a.localizations
43
+ => [<ArticleLocalization id: 1, article_id: 1, locale_id: 1, name: "Sleeping Beauty", body: "There once lived a princess locked away in a tower!">,
44
+ <ArticleLocalization id: 2, article_id: 1, locale_id: 2, name: "Belle au Bois Dormant", body: "Il était une fois une princesse enfermée dans une tour!">]
45
+
46
+ # Finder and order convenience methods are also provided:
47
+ Article.find_by_name("Snow White")
48
+ Article.find_by_body("...")
49
+ Article.find_by_name_and_body("...", "...")
50
+ Article.ordered_by_name # uses Arel, so it can be chained with other finder methods
51
+
52
+ ### `has_localization_table` Arguments
53
+ If given, the first argument is the name used for the association, otherwise it defaults to `strings`.
54
+
55
+ * `class_name` (default: base class name + "Strings"; ie. `ArticleStrings`) - the name of the localization class.
56
+ * `required` (default: false) - if true, at least a localization object for the primary language (see Configuration section) must be present or validation will fail
57
+ * `optional` (default: []) - if `required` is true, can be used to specify that specific attributes are optional
58
+
59
+ Any options that can be passed into `has_many` can also be passed along and will be used when creating the association.
60
+
61
+ ## Configuration
62
+ `HasLocalizationTable` can also be configured as follows:
63
+
64
+ HasLocalizationTable.configure do |config|
65
+ config.locale_class = "Locale"
66
+ config.locale_foreign_key = "locale_id"
67
+ config.primary_locale = Locale.primary_language
68
+ config.current_locale = Locale.current_language
69
+ config.all_locales = Locale.all
70
+ end
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "spec"
7
+ t.pattern = "spec/**/*_spec.rb"
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/has_localization_table/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Daniel Vandersluis"]
6
+ gem.email = ["dvandersluis@selfmgmt.com"]
7
+ gem.description = %q{Automatically sets up usage of a relational table to contain user-created multi-locale string attributes}
8
+ gem.summary = %q{Sets up associations and attribute methods for AR models that have a relational table to contain user-created data in multiple languages.}
9
+ gem.homepage = "https://github.com/dvandersluis/has_localization_table"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "has_localization_table"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = HasLocalizationTable::VERSION
17
+
18
+ gem.add_dependency 'activesupport', ['>= 3.0.0']
19
+ gem.add_dependency 'activerecord', ['>= 3.0.0']
20
+ gem.add_development_dependency 'minitest'
21
+ gem.add_development_dependency 'sqlite3'
22
+ end
23
+
@@ -0,0 +1,185 @@
1
+ module HasLocalizationTable
2
+ module ActiveRecord
3
+ def has_localization_table(*args)
4
+ options = args.extract_options!
5
+ options[:association_name] = args.first
6
+
7
+ class_attribute :localization_table_options
8
+ self.localization_table_options = options
9
+
10
+ extend(ClassMethods)
11
+ include(InstanceMethods)
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ def self.extended(klass)
17
+ options = { dependent: :delete_all }.merge(klass.localization_table_options)
18
+
19
+ association_name = options.delete(:association_name) || :strings
20
+
21
+ # If class_name isn't explicitly defined, try adding String onto the current class name
22
+ options[:class_name] = klass.name + "String" if options[:class_name].blank? and (Module.const_get(klass.name + "String") rescue false)
23
+
24
+ # Define the association
25
+ klass.has_many association_name, options.except(:required, :optional)
26
+ association = klass.reflect_on_association(association_name)
27
+
28
+ klass.class_eval do
29
+ # Initialize string records after main record initialization
30
+ after_initialize do
31
+ build_missing_strings
32
+ end
33
+
34
+ before_validation do
35
+ reject_empty_strings
36
+ build_missing_strings
37
+ end
38
+
39
+ # Reject any blank strings before saving the record
40
+ # Validation will have happened by this point, so if there is a required string that is needed, it won't be rejected
41
+ before_save do
42
+ reject_empty_strings
43
+ end
44
+
45
+ # Add validation to ensure a string for the primary language exists if the string is required
46
+ validate do
47
+ if self.class.localization_table_options[:required] || false
48
+ errors.add(association_name, :primary_lang_string_required) unless send(association_name).any? do |string|
49
+ string.send(HasLocalizationTable.config.locale_foreign_key) == HasLocalizationTable.primary_locale.id
50
+ end
51
+ end
52
+ end
53
+
54
+ define_method :build_missing_strings do
55
+ locale_ids = HasLocalizationTable.all_locales.map(&:id)
56
+ HasLocalizationTable.all_locales.each do |l|
57
+ send(association_name).build(HasLocalizationTable.config.locale_foreign_key => l.id) unless send(association_name).detect{ |str| str.send(HasLocalizationTable.config.locale_foreign_key) == l.id }
58
+ send(association_name).sort_by!{ |s| locale_ids.index(s.send(HasLocalizationTable.config.locale_foreign_key)) || 0 }
59
+ end
60
+ end
61
+ private :build_missing_strings
62
+
63
+ define_method :reject_empty_strings do
64
+ send(association_name).reject! { |s| !s.persisted? and self.class.localized_attributes.all?{ |attr| s.send(attr).blank? } }
65
+ end
66
+ private :reject_empty_strings
67
+
68
+ # Find a record by multiple string values
69
+ define_singleton_method :find_by_localized_attributes do |attributes, language = HasLocalizationTable.current_locale|
70
+ string_record = association.klass.where({ HasLocalizationTable.config.locale_foreign_key => language.id }.merge(attributes)).first
71
+ string_record.send(klass.to_s.underscore.to_sym) rescue nil
72
+ end
73
+ private_class_method :find_by_localized_attributes
74
+ end
75
+
76
+ klass.localized_attributes.each do |attribute|
77
+ # Add validation to make all string fields required for the primary language
78
+ association.klass.class_eval do
79
+ validates attribute, presence: { message: :custom_this_field_is_required },
80
+ if: proc { |model| klass.name.constantize.localized_attribute_required?(attribute) && model.send(HasLocalizationTable.config.locale_foreign_key) == HasLocalizationTable.current_locale.id }
81
+ end
82
+
83
+ # Set up accessors and ordering named_scopes for each non-FK attribute on the base model
84
+ klass.class_eval do
85
+ define_method attribute do |language = HasLocalizationTable.current_locale|
86
+ # Try to load a string for the given language
87
+ # If that fails, try for the primary language
88
+ get_memoized_string(language, association_name, attribute) || get_memoized_string(HasLocalizationTable.primary_locale, association_name, attribute)
89
+ end
90
+
91
+ define_method "#{attribute}=" do |value|
92
+ set_memoized_string(HasLocalizationTable.current_locale, association_name, attribute, value)
93
+ end
94
+
95
+ define_singleton_method "ordered_by_#{attribute}" do |direction = :asc|
96
+ direction = direction == :asc ? "ASC" : "DESC"
97
+
98
+ joins(%{
99
+ LEFT OUTER JOIN #{association.table_name}
100
+ ON #{association.table_name}.#{association.foreign_key} = #{self.table_name}.#{self.primary_key}
101
+ AND #{association.table_name}.#{HasLocalizationTable.config.locale_foreign_key} = %d
102
+ } % HasLocalizationTable.current_locale.id
103
+ ).
104
+ order( "#{association.table_name}.#{attribute} #{direction}")
105
+ #order{ Squeel::Nodes::Order.new(Squeel::Nodes::Stub.new(association.table_name).send(attribute), direction) }
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def localized_attributes
112
+ # Determine which attributes of the association model should be accessable through the base class
113
+ # ie. everything that's not a primary key, foreign key, or timestamp attribute
114
+ association_name = self.localization_table_options[:association_name] || :strings
115
+ association = reflect_on_association(association_name)
116
+
117
+ attribute_names = association.klass.attribute_names
118
+ timestamp_attrs = association.klass.new.send(:all_timestamp_attributes_in_model).map(&:to_s)
119
+ foreign_keys = association.klass.reflect_on_all_associations.map{ |a| a.association_foreign_key }
120
+ primary_keys = [association.klass.primary_key]
121
+ # protected_attrs = association.klass.protected_attributes.to_a
122
+
123
+ (attribute_names - timestamp_attrs - foreign_keys - primary_keys).map(&:to_sym)
124
+ end
125
+
126
+ def localized_attribute_required?(attribute)
127
+ return false unless localization_table_options[:required] || false
128
+ return true unless localization_table_options[:optional]
129
+
130
+ !localization_table_options[:optional].include?(attribute)
131
+ end
132
+
133
+ def method_missing(name, *args, &block)
134
+ if name.to_s =~ /\Afind_by_([a-z0-9_]+(_and_[a-z0-9_]+)*)\Z/
135
+ attributes = $1.split("_and_").map(&:to_sym)
136
+ if (attributes & localized_attributes).size == attributes.size and args.size == attributes.size
137
+ raise ArgumentError, "expected #{attributes.size} #{"argument".pluralize(attributes.size)}" unless args.size == attributes.size
138
+ args = attributes.zip(args).inject({}) { |memo, (key, val)| memo[key] = val; memo }
139
+ return find_by_localized_attributes(args)
140
+ end
141
+ end
142
+
143
+ super
144
+ end
145
+
146
+ def respond_to?(*args)
147
+ if args.first.to_s =~ /\Afind_by_([a-z0-9_]+(_and_[a-z0-9_]+)*)\Z/
148
+ attributes = $1.split("_and_").map(&:to_sym)
149
+ return ((attributes & localized_attributes).size == attributes.size)
150
+ end
151
+
152
+ super
153
+ end
154
+ end
155
+
156
+ module InstanceMethods
157
+ private
158
+ # Both strings and the associations are memoized, so that if an association adds more than one attribute to the main model, the association doesn't need
159
+ # to be loaded each time a different attribute is accessed.
160
+ def get_memoized_string(language, association, attribute)
161
+ @_localized_attribute_cache ||= {}
162
+ @_localized_attribute_cache[attribute] ||= {}
163
+
164
+ @_localized_association_cache ||= {}
165
+ @_localized_association_cache[association] ||= {}
166
+
167
+ @_localized_attribute_cache[attribute][language.id] ||= begin
168
+ @_localized_association_cache[association][language.id] ||= (send(association).where{ HasLocalizationTable.config.locale_foreign_key == language.id }.first || send(association).detect{ |a| a.send(HasLocalizationTable.config.locale_foreign_key) == language.id })
169
+ s = @_localized_association_cache[association][language.id].send(attribute) rescue nil
170
+ s.blank? ? nil : s
171
+ end
172
+ end
173
+
174
+ def set_memoized_string(language, association, attribute, value)
175
+ string = send(association).detect{ |a| a.send(HasLocalizationTable.config.locale_foreign_key) == language.id } || send(association).build(HasLocalizationTable.config.locale_foreign_key => language.id)
176
+ value = value.to_s
177
+
178
+ string.send(:"#{attribute}=", value)
179
+
180
+ @_localized_attribute_cache ||= {}
181
+ @_localized_attribute_cache[attribute] ||= {}
182
+ @_localized_attribute_cache[attribute][language.id] = value.blank? ? nil : value
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/configurable'
2
+ require 'active_support/inflector'
3
+
4
+ module HasLocalizationTable
5
+ # Configures global settings for HasLocalizationTable
6
+ # HasLocalizationTable.configure do |config|
7
+ # config.default_locale = Locale.find_by_code("en")
8
+ # end
9
+ def self.configure(&block)
10
+ yield @config ||= HasLocalizationTable::Configuration.new
11
+ end
12
+
13
+ # Global settings for ODF::Converter
14
+ def self.config
15
+ @config
16
+ end
17
+
18
+ # need a Class for 3.0
19
+ class Configuration #:nodoc:
20
+ include ActiveSupport::Configurable
21
+ include ActiveSupport::Inflector
22
+
23
+ config_accessor :locale_class
24
+ config_accessor :locale_foreign_key
25
+ config_accessor :primary_locale
26
+ config_accessor :current_locale
27
+ config_accessor :all_locales
28
+ end
29
+
30
+ # this is ugly. why can't we pass the default value to config_accessor...?
31
+ configure do |config|
32
+ config.locale_class = "Locale"
33
+ config.locale_foreign_key = "locale_id"
34
+ config.primary_locale = ->{ config.locale_class.constantize.first }
35
+ config.current_locale = ->{ config.locale_class.constantize.first }
36
+ config.all_locales = ->{ config.locale_class.constantize.all }
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module HasLocalizationTable
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,15 @@
1
+ require "has_localization_table/version"
2
+ require "has_localization_table/config"
3
+ require "has_localization_table/active_record"
4
+
5
+ ActiveRecord::Base.extend(HasLocalizationTable::ActiveRecord) if defined?(ActiveRecord::Base)
6
+
7
+ module HasLocalizationTable
8
+ [:primary_locale, :current_locale, :all_locales].each do |meth|
9
+ define_singleton_method meth do
10
+ l = config.send(meth)
11
+ return l.call if l.respond_to?(:call)
12
+ l
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+
3
+ # Setup in-memory database so AR can work
4
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
5
+ ActiveRecord::Migration.tap do |m|
6
+ m.create_table :articles do |t|
7
+ t.timestamps
8
+ end
9
+
10
+ m.create_table :locales do |t|
11
+ t.string :name
12
+ end
13
+
14
+ m.create_table :article_strings do |t|
15
+ t.integer :article_id
16
+ t.integer :locale_id
17
+ t.string :name
18
+ t.string :description
19
+ end
20
+ end
21
+
22
+ # Set up locales
23
+ Locale = Class.new(ActiveRecord::Base)
24
+ Locale.create!(name: "English")
25
+ Locale.create!(name: "French")
26
+
27
+ ArticleString = Class.new(ActiveRecord::Base) do
28
+ belongs_to :article
29
+ belongs_to :locale
30
+ end
31
+
32
+ describe HasLocalizationTable do
33
+ before do
34
+ # Configure HLT
35
+ HasLocalizationTable.configure do |c|
36
+ c.primary_locale = Locale.first
37
+ c.current_locale = Locale.first
38
+ c.all_locales = Locale.all
39
+ end
40
+ end
41
+
42
+ describe "#has_localization_table" do
43
+ before do
44
+ Object.send(:remove_const, :Article) rescue nil
45
+ Article = Class.new(ActiveRecord::Base)
46
+ end
47
+
48
+ it "should track any given options" do
49
+ Article.has_localization_table :localizations, required: true, optional: [:description]
50
+ Article.localization_table_options.must_equal({ association_name: :localizations, required: true, optional: [:description] })
51
+ end
52
+
53
+ it "should define has_many association on the base class with a default name of :strings" do
54
+ Article.has_localization_table
55
+ assoc = Article.reflect_on_association(:strings)
56
+ assoc.wont_be_nil
57
+ assoc.macro.must_equal :has_many
58
+ assoc.klass.must_equal ArticleString
59
+ end
60
+
61
+ it "should use the given association name" do
62
+ Article.has_localization_table :localizations
63
+ assoc = Article.reflect_on_association(:localizations)
64
+ assoc.wont_be_nil
65
+ assoc.macro.must_equal :has_many
66
+ assoc.klass.must_equal ArticleString
67
+ end
68
+
69
+ it "should use the given class" do
70
+ ArticleText = Class.new(ArticleString)
71
+ Article.has_localization_table class_name: ArticleText
72
+ assoc = Article.reflect_on_association(:strings)
73
+ assoc.wont_be_nil
74
+ assoc.macro.must_equal :has_many
75
+ assoc.klass.name.must_equal "ArticleText"
76
+
77
+ Object.send(:remove_const, :ArticleText)
78
+ end
79
+
80
+ it "should add validations if given required: true" do
81
+ Article.has_localization_table required: true
82
+ a = Article.new
83
+ refute a.valid?
84
+ a.errors[:strings].wont_be_empty
85
+
86
+ a = Article.new(description: "Wishing the world hello!")
87
+ s = a.strings.first
88
+ refute s.valid?
89
+ s.errors[:name].wont_be_empty
90
+ end
91
+
92
+ it "should not add validations if given required: false" do
93
+ Article.has_localization_table required: false
94
+ a = Article.new
95
+ a.valid? or raise a.strings.map(&:errors).inspect
96
+ a.errors[:strings].must_be_empty
97
+
98
+ a = Article.new(description: "Wishing the world hello!")
99
+ s = a.strings.first
100
+ assert s.valid?
101
+ s.errors[:name].must_be_empty
102
+ end
103
+
104
+ it "should not add validations if required is not given" do
105
+ Article.has_localization_table
106
+ a = Article.new
107
+ assert a.valid?
108
+ a.errors[:strings].must_be_empty
109
+
110
+ a = Article.new(description: "Wishing the world hello!")
111
+ s = a.strings.first
112
+ assert s.valid?
113
+ s.errors[:name].must_be_empty
114
+ end
115
+
116
+ it "should not add validations for optional fields" do
117
+ Article.has_localization_table required: true, optional: [:description]
118
+ a = Article.new(name: "Test")
119
+ assert a.valid?
120
+ a.errors[:strings].must_be_empty
121
+ assert a.strings.all?{ |s| s.errors[:description].empty? }
122
+ end
123
+ end
124
+
125
+ describe "other methods" do
126
+ before do
127
+ Object.send(:remove_const, :Article) rescue nil
128
+ Article = Class.new(ActiveRecord::Base)
129
+ Article.has_localization_table
130
+ end
131
+
132
+ let(:a) { Article.new(name: "Test", description: "Description") }
133
+
134
+ it "should set localized attributes" do
135
+ a.strings.first.name.must_equal "Test"
136
+ a.strings.first.description.must_equal "Description"
137
+ end
138
+
139
+ it "should create accessor methods" do
140
+ a.name.must_equal "Test"
141
+ a.description.must_equal "Description"
142
+ end
143
+
144
+ it "should save localized attributes" do
145
+ a.save!
146
+ a.reload
147
+ a.name.must_equal "Test"
148
+ a.description.must_equal "Description"
149
+ end
150
+
151
+ it "should create mutator methods" do
152
+ a.name = "Changed"
153
+ a.description = "Changed Description"
154
+ a.name.must_equal "Changed"
155
+ a.description.must_equal "Changed Description"
156
+ a.strings.first.name.must_equal "Changed"
157
+ a.strings.first.description.must_equal "Changed Description"
158
+ end
159
+
160
+ it "should use the current locale when setting" do
161
+ a
162
+
163
+ HasLocalizationTable.configure do |c|
164
+ c.current_locale = Locale.last
165
+ end
166
+
167
+ a.name = "French Name"
168
+ a.description = "French Description"
169
+
170
+ eng = a.strings.detect{ |s| s.locale_id == Locale.first.id }
171
+ fre = a.strings.detect{ |s| s.locale_id == Locale.last.id }
172
+
173
+ eng.name.must_equal "Test"
174
+ eng.description.must_equal "Description"
175
+ fre.name.must_equal "French Name"
176
+ fre.description.must_equal "French Description"
177
+ end
178
+
179
+ it "should create finder methods" do
180
+ a.save!
181
+ Article.find_by_name("Test").must_equal a
182
+ Article.find_by_description("Description").must_equal a
183
+ Article.find_by_name_and_description("Test", "Description").must_equal a
184
+ Article.find_by_description_and_name("Description", "Test").must_equal a
185
+
186
+ Article.find_by_name("Wrong").must_be_nil
187
+ Article.find_by_description("Wrong").must_be_nil
188
+ end
189
+
190
+ it "should create ordered_by methods" do
191
+ a.save!
192
+ b = Article.create!(name: "Name", description: "Another Description")
193
+ c = Article.create!(name: "Once Upon a Time...", description: "Fairytale")
194
+ Article.ordered_by_name.must_equal [b, c, a]
195
+ Article.ordered_by_description.must_equal [b, a, c]
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+ require 'active_record'
3
+ require 'has_localization_table'
4
+
5
+ class MiniTest::Spec
6
+ def run(*args, &block)
7
+ value = nil
8
+
9
+ begin
10
+ ActiveRecord::Base.connection.transaction do
11
+ value = super
12
+ raise ActiveRecord::Rollback
13
+ end
14
+ rescue ActiveRecord::Rollback
15
+ end
16
+
17
+ return value # The result of run must be always returned for the pretty dots to show up
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_localization_table
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Vandersluis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: &21307760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *21307760
25
+ - !ruby/object:Gem::Dependency
26
+ name: activerecord
27
+ requirement: &21323460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 3.0.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *21323460
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &21322860 !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: *21322860
47
+ - !ruby/object:Gem::Dependency
48
+ name: sqlite3
49
+ requirement: &21322440 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *21322440
58
+ description: Automatically sets up usage of a relational table to contain user-created
59
+ multi-locale string attributes
60
+ email:
61
+ - dvandersluis@selfmgmt.com
62
+ executables: []
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - Gemfile
68
+ - LICENSE
69
+ - README.md
70
+ - Rakefile
71
+ - has_localization_table.gemspec
72
+ - lib/has_localization_table.rb
73
+ - lib/has_localization_table/active_record.rb
74
+ - lib/has_localization_table/config.rb
75
+ - lib/has_localization_table/version.rb
76
+ - spec/active_record_spec.rb
77
+ - spec/spec_helper.rb
78
+ homepage: https://github.com/dvandersluis/has_localization_table
79
+ licenses: []
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 1.8.12
99
+ signing_key:
100
+ specification_version: 3
101
+ summary: Sets up associations and attribute methods for AR models that have a relational
102
+ table to contain user-created data in multiple languages.
103
+ test_files:
104
+ - spec/active_record_spec.rb
105
+ - spec/spec_helper.rb
106
+ has_rdoc: