has_localization_table 0.0.2

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,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: