has_localization_table 0.0.4 → 0.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/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