has_localization_table 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -52,21 +52,37 @@ The gem assumes that the localization table has already been migrated, and the m
52
52
  ### `has_localization_table` Arguments
53
53
  If given, the first argument is the name used for the association, otherwise it defaults to `strings`.
54
54
 
55
- * `class_name` (default: base class name + "Strings"; ie. `ArticleStrings`) - the name of the localization class.
55
+ * `class_name` (default: base class name + class_suffix) - the name of the localization class.
56
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
57
  * `optional` (default: []) - if `required` is true, can be used to specify that specific attributes are optional
58
58
 
59
59
  Any options that can be passed into `has_many` can also be passed along and will be used when creating the association.
60
60
 
61
61
  ## Configuration
62
- `HasLocalizationTable` can also be configured as follows:
62
+ `HasLocalizationTable` can also be configured as follows. Note that if any configuration option responds to `call`, it will be called.
63
63
 
64
64
  HasLocalizationTable.configure do |config|
65
+ # Default suffix to use for Localization class names
66
+ # ie. Article -> ArticleLocalization
67
+ config.class_suffix = "Localization"
68
+
69
+ # Default localizations association name
70
+ config.default_association_name = :localizations
71
+
72
+ # Class name for Locale objects
65
73
  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
74
+
75
+ # Foreign key used in localization tables to relate to a Locale
76
+ config.locale_foreign_key = "locale_id"
77
+
78
+ # Primary (main) locale
79
+ config.primary_locale = ->{ Locale.primary_language }
80
+
81
+ # Current locale
82
+ config.current_locale = ->{ Locale.current_language }
83
+
84
+ # All available locales
85
+ config.all_locales = ->{ Locale.all }
70
86
  end
71
87
 
72
88
  ## Contributing
@@ -1,15 +1,17 @@
1
1
  require "has_localization_table/version"
2
2
  require "has_localization_table/config"
3
+ require "has_localization_table/class_methods"
4
+ require "has_localization_table/instance_methods"
3
5
  require "has_localization_table/active_record"
4
6
 
5
7
  ActiveRecord::Base.extend(HasLocalizationTable::ActiveRecord) if defined?(ActiveRecord::Base)
6
8
 
7
9
  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
10
+ HasLocalizationTable.config.config.keys.each do |key|
11
+ define_singleton_method key do
12
+ val = config.send(key)
13
+ val = val.call if val.respond_to?(:call)
14
+ val
13
15
  end
14
16
  end
15
17
  end
@@ -2,184 +2,17 @@ module HasLocalizationTable
2
2
  module ActiveRecord
3
3
  def has_localization_table(*args)
4
4
  options = args.extract_options!
5
- options[:association_name] = args.first
5
+ options[:association_name] = args.first || HasLocalizationTable.default_association_name
6
6
 
7
7
  class_attribute :localization_table_options
8
- self.localization_table_options = options
8
+ self.localization_table_options = { dependent: :delete_all, class_name: localization_class.name }.merge(options)
9
9
 
10
10
  extend(ClassMethods)
11
11
  include(InstanceMethods)
12
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 locale 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, locale = HasLocalizationTable.current_locale|
70
- string_record = association.klass.where({ HasLocalizationTable.config.locale_foreign_key => locale.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 locale
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 |locale = HasLocalizationTable.current_locale|
86
- # Try to load a string for the given locale
87
- # If that fails, try for the primary locale
88
- get_cached_localized_attribute(locale, association_name, attribute) || get_cached_localized_attribute(HasLocalizationTable.primary_locale, association_name, attribute)
89
- end
90
-
91
- define_method "#{attribute}=" do |value|
92
- cache_localized_attribute(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
13
 
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_cached_localized_attribute(locale, 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][locale.id] ||= begin
168
- @_localized_association_cache[association][locale.id] ||= send(association).detect{ |a| a.send(HasLocalizationTable.config.locale_foreign_key) == locale.id }
169
- s = @_localized_association_cache[association][locale.id].send(attribute) rescue nil
170
- s.blank? ? nil : s
171
- end
172
- end
173
-
174
- def cache_localized_attribute(locale, association, attribute, value)
175
- string = send(association).detect{ |a| a.send(HasLocalizationTable.config.locale_foreign_key) == locale.id } || send(association).build(HasLocalizationTable.config.locale_foreign_key => locale.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][locale.id] = value.blank? ? nil : value
14
+ def localization_class
15
+ (self.name + HasLocalizationTable.class_suffix).constantize
183
16
  end
184
17
  end
185
18
  end
@@ -0,0 +1,121 @@
1
+ module HasLocalizationTable
2
+ module ClassMethods
3
+ def self.extended(klass)
4
+ klass.class_eval do
5
+ create_localization_association!
6
+
7
+ # Initialize string records after main record initialization
8
+ after_initialize do
9
+ build_missing_localizations!
10
+ end
11
+
12
+ before_validation do
13
+ reject_empty_localizations!
14
+ build_missing_localizations!
15
+ end
16
+
17
+ # Reject any blank strings before saving the record
18
+ # Validation will have happened by this point, so if there is a required string that is needed, it won't be rejected
19
+ before_save do
20
+ reject_empty_localizations!
21
+ end
22
+
23
+ # Add validation to ensure a string for the primary locale exists if the string is required
24
+ validate do
25
+ if localization_table_options[:required] || false
26
+ errors.add(localization_association_name, :primary_lang_string_required) unless localization_association.any? do |string|
27
+ string.send(HasLocalizationTable.locale_foreign_key) == HasLocalizationTable.primary_locale.id
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ klass.localized_attributes.each do |attribute|
34
+ # Add validation to make all string fields required for the primary locale
35
+ klass.send(:localization_class).class_eval do
36
+ validates attribute, presence: { message: :custom_this_field_is_required },
37
+ if: proc { |model| klass.name.constantize.localized_attribute_required?(attribute) && model.send(HasLocalizationTable.locale_foreign_key) == HasLocalizationTable.current_locale.id }
38
+ end
39
+ end
40
+
41
+ # Alias the scoping method to use the actual association name
42
+ alias_method :"with_#{klass.localization_association_name}", :with_localizations
43
+ end
44
+
45
+ def localization_association_name
46
+ localization_table_options[:association_name]
47
+ end
48
+
49
+ def localized_attributes
50
+ # Determine which attributes of the association model should be accessable through the base class
51
+ # ie. everything that's not a primary key, foreign key, or timestamp attribute
52
+ association_name = self.localization_table_options[:association_name] || :strings
53
+ association = reflect_on_association(association_name)
54
+
55
+ attribute_names = association.klass.attribute_names
56
+ timestamp_attrs = association.klass.new.send(:all_timestamp_attributes_in_model).map(&:to_s)
57
+ foreign_keys = association.klass.reflect_on_all_associations.map{ |a| a.association_foreign_key }
58
+ primary_keys = [association.klass.primary_key]
59
+ # protected_attrs = association.klass.protected_attributes.to_a
60
+
61
+ (attribute_names - timestamp_attrs - foreign_keys - primary_keys).map(&:to_sym)
62
+ end
63
+
64
+ def localized_attribute_required?(attribute)
65
+ return false unless localization_table_options[:required] || false
66
+ return true unless localization_table_options[:optional]
67
+
68
+ !localization_table_options[:optional].include?(attribute)
69
+ end
70
+
71
+ def method_missing(name, *args, &block)
72
+ if name.to_s =~ /\Afind_by_([a-z0-9_]+(_and_[a-z0-9_]+)*)\Z/
73
+ attributes = $1.split("_and_").map(&:to_sym)
74
+ if (attributes & localized_attributes).size == attributes.size
75
+ raise ArgumentError, "expected #{attributes.size} #{"argument".pluralize(attributes.size)}: #{attributes.join(", ")}" unless args.size == attributes.size
76
+ args = attributes.zip(args).inject({}) { |memo, (key, val)| memo[key] = val; memo }
77
+ return find_by_localized_attributes(args)
78
+ end
79
+ elsif name.to_s =~ /\Aordered_by_([a-z0-9_]+)\Z/
80
+ attribute = $1.to_sym
81
+ return ordered_by_localized_attribute(attribute, *args) if localized_attributes.include?(attribute)
82
+ end
83
+
84
+ super
85
+ end
86
+
87
+ def respond_to?(*args)
88
+ if args.first.to_s =~ /\Afind_by_([a-z0-9_]+(_and_[a-z0-9_]+)*)\Z/
89
+ attributes = $1.split("_and_").map(&:to_sym)
90
+ return true if (attributes & localized_attributes).size == attributes.size
91
+ elsif name.to_s =~ /\Aordered_by_([a-z0-9_]+)\Z/
92
+ return true if localized_attributes.include?($1.to_sym)
93
+ end
94
+
95
+ super
96
+ end
97
+
98
+ def with_localizations
99
+ scoped.joins((<<-eoq % HasLocalizationTable.current_locale.id).gsub(/\s+/, " "))
100
+ LEFT OUTER JOIN #{localization_class.table_name}
101
+ ON #{localization_class.table_name}.#{self.name.underscore}_id = #{self.table_name}.#{self.primary_key}
102
+ AND #{localization_class.table_name}.#{HasLocalizationTable.locale_foreign_key} = %d
103
+ eoq
104
+ end
105
+
106
+ private
107
+ def create_localization_association!
108
+ self.has_many localization_association_name, localization_table_options.except(:association_name, :required, :optional)
109
+ end
110
+
111
+ # Find a record by multiple localization values
112
+ def find_by_localized_attributes(attributes, locale = HasLocalizationTable.current_locale)
113
+ with_localizations.where(localization_class.table_name => attributes).first
114
+ end
115
+
116
+ # Order records by localization value
117
+ def ordered_by_localized_attribute(attribute, asc = true, locale = HasLocalizationTable.current_locale)
118
+ with_localizations.order("#{localization_class.table_name}.#{attribute} #{asc ? "ASC" : "DESC"}")
119
+ end
120
+ end
121
+ end
@@ -25,12 +25,16 @@ module HasLocalizationTable
25
25
  config_accessor :primary_locale
26
26
  config_accessor :current_locale
27
27
  config_accessor :all_locales
28
+ config_accessor :class_suffix
29
+ config_accessor :default_association_name
28
30
  end
29
31
 
30
32
  # this is ugly. why can't we pass the default value to config_accessor...?
31
33
  configure do |config|
32
34
  config.locale_class = "Locale"
33
35
  config.locale_foreign_key = "locale_id"
36
+ config.class_suffix = "Localization"
37
+ config.default_association_name = :localizations
34
38
  config.primary_locale = ->{ config.locale_class.constantize.first }
35
39
  config.current_locale = ->{ config.locale_class.constantize.first }
36
40
  config.all_locales = ->{ config.locale_class.constantize.all }
@@ -0,0 +1,78 @@
1
+ module HasLocalizationTable
2
+ module InstanceMethods
3
+ def read_localized_attribute(attribute, locale = HasLocalizationTable.current_locale)
4
+ attribute_cache[attribute.to_sym][locale.id] ||= localization_association.detect{ |a| a.send(HasLocalizationTable.locale_foreign_key) == locale.id }.send(attribute) rescue nil
5
+ end
6
+
7
+ def write_localized_attribute(attribute, value, locale = HasLocalizationTable.current_locale)
8
+ value = value.to_s
9
+ localization = localization_association.detect{ |a| a.send(HasLocalizationTable.locale_foreign_key) == locale.id } ||
10
+ localization_association.build(HasLocalizationTable.locale_foreign_key => locale.id)
11
+
12
+ localization.send(:"#{attribute}=", value)
13
+ attribute_cache[attribute.to_sym][locale.id] = value.blank? ? nil : value
14
+ end
15
+
16
+ # Define attribute getters and setters
17
+ def method_missing(name, *args, &block)
18
+ if name.to_s =~ /\A([a-z0-9_]+)(=)?\Z/i
19
+ if localized_attributes.include?($1.to_sym)
20
+ if $2.nil? # No equals sign -- not a setter
21
+ # Try to load a string for the given locale
22
+ # If that fails, try for the primary locale
23
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0 or 1)" unless args.size.between?(0, 1)
24
+ return read_localized_attribute($1, args.first) || read_localized_attribute($1, HasLocalizationTable.primary_locale)
25
+ else
26
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" unless args.size == 1
27
+ return write_localized_attribute($1, args.first)
28
+ end
29
+ end
30
+ end
31
+
32
+ super
33
+ end
34
+
35
+ def respond_to?(*args)
36
+ return true if args.first.to_s =~ /\A([a-z0-9_]+)=?\Z/i and localized_attributes.include?($1.to_sym)
37
+ super
38
+ end
39
+
40
+ private
41
+ # Add localization objects for any available locale that doesn't have one
42
+ def build_missing_localizations!
43
+ locale_ids = HasLocalizationTable.all_locales.map(&:id)
44
+ HasLocalizationTable.all_locales.each do |l|
45
+ unless localization_association.detect{ |str| str.send(HasLocalizationTable.locale_foreign_key) == l.id }
46
+ localization_association.build(HasLocalizationTable.locale_foreign_key => l.id)
47
+ end
48
+
49
+ localization_association.sort_by!{ |l| locale_ids.index(l.send(HasLocalizationTable.locale_foreign_key)) || 0 }
50
+ end
51
+ end
52
+
53
+ # Remove localization objects that are not filled in
54
+ def reject_empty_localizations!
55
+ localization_association.reject! { |l| !l.persisted? and localized_attributes.all?{ |attr| l.send(attr).blank? } }
56
+ end
57
+
58
+ # Helper method for getting the localization association without having to look up the name each time
59
+ def localization_association
60
+ @localization_association ||= begin
61
+ association_name = localization_table_options[:association_name]
62
+ send(association_name)
63
+ end
64
+ end
65
+
66
+ def attribute_cache
67
+ @localized_attribute_cache ||= localized_attributes.inject({}) { |memo, attr| memo[attr] = {}; memo }
68
+ end
69
+
70
+ def localized_attributes
71
+ self.class.localized_attributes
72
+ end
73
+
74
+ def localization_table_options
75
+ self.class.localization_table_options
76
+ end
77
+ end
78
+ end
@@ -1,3 +1,3 @@
1
1
  module HasLocalizationTable
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -11,7 +11,7 @@ ActiveRecord::Migration.tap do |m|
11
11
  t.string :name
12
12
  end
13
13
 
14
- m.create_table :article_strings do |t|
14
+ m.create_table :article_localizations do |t|
15
15
  t.integer :article_id
16
16
  t.integer :locale_id
17
17
  t.string :name
@@ -24,7 +24,7 @@ Locale = Class.new(ActiveRecord::Base)
24
24
  Locale.create!(name: "English")
25
25
  Locale.create!(name: "French")
26
26
 
27
- ArticleString = Class.new(ActiveRecord::Base) do
27
+ ArticleLocalization = Class.new(ActiveRecord::Base) do
28
28
  belongs_to :article
29
29
  belongs_to :locale
30
30
  end
@@ -46,33 +46,33 @@ describe HasLocalizationTable do
46
46
  end
47
47
 
48
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] })
49
+ Article.has_localization_table :strings, required: true, optional: [:description]
50
+ Article.localization_table_options.slice(:association_name, :required, :optional).must_equal({ association_name: :strings, required: true, optional: [:description] })
51
51
  end
52
52
 
53
- it "should define has_many association on the base class with a default name of :strings" do
53
+ it "should define has_many association on the base class with a default name of :localizations" do
54
54
  Article.has_localization_table
55
- assoc = Article.reflect_on_association(:strings)
55
+ assoc = Article.reflect_on_association(:localizations)
56
56
  assoc.wont_be_nil
57
57
  assoc.macro.must_equal :has_many
58
- assoc.klass.must_equal ArticleString
58
+ assoc.klass.must_equal ArticleLocalization
59
59
  end
60
60
 
61
61
  it "should use the given association name" do
62
- Article.has_localization_table :localizations
63
- assoc = Article.reflect_on_association(:localizations)
62
+ Article.has_localization_table :strings
63
+ assoc = Article.reflect_on_association(:strings)
64
64
  assoc.wont_be_nil
65
65
  assoc.macro.must_equal :has_many
66
- assoc.klass.must_equal ArticleString
66
+ assoc.klass.must_equal ArticleLocalization
67
67
  end
68
68
 
69
69
  it "should use the given class" do
70
- ArticleText = Class.new(ArticleString)
70
+ ArticleText = Class.new(ArticleLocalization)
71
71
  Article.has_localization_table class_name: ArticleText
72
- assoc = Article.reflect_on_association(:strings)
72
+ assoc = Article.reflect_on_association(:localizations)
73
73
  assoc.wont_be_nil
74
74
  assoc.macro.must_equal :has_many
75
- assoc.klass.name.must_equal "ArticleText"
75
+ assoc.klass.must_equal ArticleText
76
76
 
77
77
  Object.send(:remove_const, :ArticleText)
78
78
  end
@@ -81,10 +81,10 @@ describe HasLocalizationTable do
81
81
  Article.has_localization_table required: true
82
82
  a = Article.new
83
83
  refute a.valid?
84
- a.errors[:strings].wont_be_empty
84
+ a.errors[:localizations].wont_be_empty
85
85
 
86
86
  a = Article.new(description: "Wishing the world hello!")
87
- s = a.strings.first
87
+ s = a.localizations.first
88
88
  refute s.valid?
89
89
  s.errors[:name].wont_be_empty
90
90
  end
@@ -92,11 +92,11 @@ describe HasLocalizationTable do
92
92
  it "should not add validations if given required: false" do
93
93
  Article.has_localization_table required: false
94
94
  a = Article.new
95
- a.valid? or raise a.strings.map(&:errors).inspect
96
- a.errors[:strings].must_be_empty
95
+ a.valid? or raise a.localizations.map(&:errors).inspect
96
+ a.errors[:localizations].must_be_empty
97
97
 
98
98
  a = Article.new(description: "Wishing the world hello!")
99
- s = a.strings.first
99
+ s = a.localizations.first
100
100
  assert s.valid?
101
101
  s.errors[:name].must_be_empty
102
102
  end
@@ -105,10 +105,10 @@ describe HasLocalizationTable do
105
105
  Article.has_localization_table
106
106
  a = Article.new
107
107
  assert a.valid?
108
- a.errors[:strings].must_be_empty
108
+ a.errors[:localizations].must_be_empty
109
109
 
110
110
  a = Article.new(description: "Wishing the world hello!")
111
- s = a.strings.first
111
+ s = a.localizations.first
112
112
  assert s.valid?
113
113
  s.errors[:name].must_be_empty
114
114
  end
@@ -117,8 +117,8 @@ describe HasLocalizationTable do
117
117
  Article.has_localization_table required: true, optional: [:description]
118
118
  a = Article.new(name: "Test")
119
119
  assert a.valid?
120
- a.errors[:strings].must_be_empty
121
- assert a.strings.all?{ |s| s.errors[:description].empty? }
120
+ a.errors[:localizations].must_be_empty
121
+ assert a.localizations.all?{ |s| s.errors[:description].empty? }
122
122
  end
123
123
  end
124
124
 
@@ -132,8 +132,8 @@ describe HasLocalizationTable do
132
132
  let(:a) { Article.new(name: "Test", description: "Description") }
133
133
 
134
134
  it "should set localized attributes" do
135
- a.strings.first.name.must_equal "Test"
136
- a.strings.first.description.must_equal "Description"
135
+ a.localizations.first.name.must_equal "Test"
136
+ a.localizations.first.description.must_equal "Description"
137
137
  end
138
138
 
139
139
  it "should create accessor methods" do
@@ -153,8 +153,8 @@ describe HasLocalizationTable do
153
153
  a.description = "Changed Description"
154
154
  a.name.must_equal "Changed"
155
155
  a.description.must_equal "Changed Description"
156
- a.strings.first.name.must_equal "Changed"
157
- a.strings.first.description.must_equal "Changed Description"
156
+ a.localizations.first.name.must_equal "Changed"
157
+ a.localizations.first.description.must_equal "Changed Description"
158
158
  end
159
159
 
160
160
  it "should use the current locale when setting" do
@@ -167,8 +167,8 @@ describe HasLocalizationTable do
167
167
  a.name = "French Name"
168
168
  a.description = "French Description"
169
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 }
170
+ eng = a.localizations.detect{ |s| s.locale_id == Locale.first.id }
171
+ fre = a.localizations.detect{ |s| s.locale_id == Locale.last.id }
172
172
 
173
173
  eng.name.must_equal "Test"
174
174
  eng.description.must_equal "Description"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_localization_table
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2012-08-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &21033000 !ruby/object:Gem::Requirement
16
+ requirement: &11803100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *21033000
24
+ version_requirements: *11803100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activerecord
27
- requirement: &21032340 !ruby/object:Gem::Requirement
27
+ requirement: &11802200 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 3.0.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *21032340
35
+ version_requirements: *11802200
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: minitest
38
- requirement: &21031960 !ruby/object:Gem::Requirement
38
+ requirement: &11800680 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *21031960
46
+ version_requirements: *11800680
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sqlite3
49
- requirement: &21031480 !ruby/object:Gem::Requirement
49
+ requirement: &11866300 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *21031480
57
+ version_requirements: *11866300
58
58
  description: Automatically sets up usage of a relational table to contain user-created
59
59
  multi-locale string attributes
60
60
  email:
@@ -71,7 +71,9 @@ files:
71
71
  - has_localization_table.gemspec
72
72
  - lib/has_localization_table.rb
73
73
  - lib/has_localization_table/active_record.rb
74
+ - lib/has_localization_table/class_methods.rb
74
75
  - lib/has_localization_table/config.rb
76
+ - lib/has_localization_table/instance_methods.rb
75
77
  - lib/has_localization_table/version.rb
76
78
  - spec/active_record_spec.rb
77
79
  - spec/spec_helper.rb