has_localization_table 0.3.13 → 0.4.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.
- checksums.yaml +8 -8
- data/lib/has_localization_table/active_record/callbacks.rb +16 -4
- data/lib/has_localization_table/active_record/meta_methods.rb +12 -12
- data/lib/has_localization_table/active_record/relation.rb +14 -8
- data/lib/has_localization_table/version.rb +1 -1
- data/spec/active_record/callbacks_spec.rb +51 -0
- data/spec/active_record/relation_spec.rb +24 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZDVjZmEwMjUwZTk5OTMxMWJhZmQ4MmJhNTk5NTFjNDZhNjcxNDk3OQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Y2ExNzJkNWJkMTY0MmQyMDM4MmQ5Y2YzYTVlNWE5YWUwMzhkYjg1ZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTQzNGFhMThhMmZmY2NiMjliOTJlYmQ2YWVmYjM3YTYzM2E0ZDdmNTA0YzI4
|
10
|
+
OTdkODNmYzRhYjNiNGIyNTdkMGIwYzRjMTBjMzZkNmQ3MjczMTUyZmQ5NmVj
|
11
|
+
ODQyZTQ5N2JjYTcwZjYwMDgwNTY3ZTBkNDM4MWUxMmZjNzE1ZTc=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZDkwZGMxODU0ODNiNjNkMzVmNGRmYzYyODBhYmUzMjgyNTViMDYxYjgxY2E2
|
14
|
+
YzM1MDY5OGNkZTVlYWFkMTYyMjdjZGI2MWMzODFlYTE4OGU3NGY2NTRlYzYz
|
15
|
+
NDcwMzViNTc5Yjg4MTgxNGNkN2QzMTUwYWEzYTBiMzA1ZDc0ODQ=
|
@@ -1,17 +1,21 @@
|
|
1
1
|
module HasLocalizationTable
|
2
2
|
module ActiveRecord
|
3
3
|
module Callbacks
|
4
|
+
def self.extended(klass)
|
5
|
+
klass.send(:include, InstanceMethods)
|
6
|
+
end
|
7
|
+
|
4
8
|
def setup_localization_callbacks!
|
5
9
|
# Initialize string records after main record initialization
|
6
|
-
after_initialize do
|
10
|
+
after_initialize(if: :add_localization_after_initialize?) do
|
7
11
|
build_missing_localizations!
|
8
12
|
end
|
9
|
-
|
13
|
+
|
10
14
|
before_validation do
|
11
15
|
reject_empty_localizations!
|
12
16
|
build_missing_localizations!
|
13
17
|
end
|
14
|
-
|
18
|
+
|
15
19
|
# Reject any blank strings before saving the record
|
16
20
|
# Validation will have happened by this point, so if there is a required string that is needed, it won't be rejected
|
17
21
|
before_save do
|
@@ -19,6 +23,14 @@ module HasLocalizationTable
|
|
19
23
|
end
|
20
24
|
end
|
21
25
|
private :setup_localization_callbacks!
|
26
|
+
|
27
|
+
module InstanceMethods
|
28
|
+
def add_localization_after_initialize?
|
29
|
+
localization_table_options.fetch(:initialize, true) && !localization_table_options.fetch(:include, false)
|
30
|
+
end
|
31
|
+
|
32
|
+
private :add_localization_after_initialize?
|
33
|
+
end
|
22
34
|
end
|
23
35
|
end
|
24
|
-
end
|
36
|
+
end
|
@@ -4,52 +4,52 @@ module HasLocalizationTable
|
|
4
4
|
def self.extended(klass)
|
5
5
|
klass.send(:include, InstanceMethods)
|
6
6
|
end
|
7
|
-
|
7
|
+
|
8
8
|
def localization_class
|
9
9
|
localization_table_options[:class_name].constantize
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def localization_association_name
|
13
13
|
localization_table_options[:association_name]
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def localized_attributes
|
17
17
|
# Determine which attributes of the association model should be accessible through the base class
|
18
18
|
# ie. everything that's not a primary key, foreign key, or timestamp attribute
|
19
19
|
association_name = self.localization_table_options[:association_name] || :strings
|
20
20
|
association = reflect_on_association(association_name)
|
21
|
-
|
21
|
+
|
22
22
|
attribute_names = association.klass.attribute_names
|
23
23
|
timestamp_attrs = association.klass.new.send(:all_timestamp_attributes_in_model).map(&:to_s)
|
24
24
|
foreign_keys = association.klass.reflect_on_all_associations.map{ |a| a.association_foreign_key }
|
25
25
|
primary_keys = [association.klass.primary_key]
|
26
26
|
# protected_attrs = association.klass.protected_attributes.to_a
|
27
|
-
|
27
|
+
|
28
28
|
(attribute_names - timestamp_attrs - foreign_keys - primary_keys).map(&:to_sym)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
def localized_attribute_required?(attribute)
|
32
32
|
return false unless localization_table_options[:required] || false
|
33
33
|
return true unless localization_table_options[:optional]
|
34
|
-
|
35
|
-
!localization_table_options[:optional].include?(attribute)
|
34
|
+
|
35
|
+
!localization_table_options[:optional].include?(attribute)
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
module InstanceMethods
|
39
39
|
# Helper method for getting the localization association without having to look up the name each time
|
40
40
|
def localization_association
|
41
41
|
association_name = localization_table_options[:association_name]
|
42
42
|
send(association_name)
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def localized_attributes
|
46
46
|
self.class.localized_attributes
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
def localization_table_options
|
50
50
|
self.class.localization_table_options
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
-
end
|
55
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module HasLocalizationTable
|
2
2
|
module ActiveRecord
|
3
3
|
module Relation
|
4
|
+
RESERVED_KEYS = [:association_name, :required, :optional, :dependent, :has_one, :initialize, :include]
|
5
|
+
|
4
6
|
def self.extended(klass)
|
5
7
|
klass.send(:include, InstanceMethods)
|
6
8
|
klass.send(:create_localization_associations!)
|
@@ -8,10 +10,10 @@ module HasLocalizationTable
|
|
8
10
|
# Alias the scoping method to use the actual association name
|
9
11
|
alias_method :"with_#{klass.localization_association_name}", :with_localizations
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def with_localizations(locale = HasLocalizationTable.current_locale.id)
|
13
15
|
lcat = localization_class.arel_table
|
14
|
-
|
16
|
+
|
15
17
|
scoped.joins(
|
16
18
|
arel_table.join(lcat, Arel::Nodes::OuterJoin).
|
17
19
|
on(lcat[:"#{self.name.underscore}_id"].eq(arel_table[self.primary_key]).and(lcat[HasLocalizationTable.locale_foreign_key].eq(locale))).
|
@@ -37,7 +39,7 @@ module HasLocalizationTable
|
|
37
39
|
association_name = localization_association_name.to_s.singularize.to_sym
|
38
40
|
association_name = :localization if localized_attributes.include?(association_name)
|
39
41
|
|
40
|
-
has_one_options = localization_table_options.except(
|
42
|
+
has_one_options = localization_table_options.except(*RESERVED_KEYS).
|
41
43
|
merge({ conditions: Proc.new { "#{table_name}.#{foreign_key} = #{HasLocalizationTable.current_locale.id}"} })
|
42
44
|
|
43
45
|
self.has_one association_name, has_one_options
|
@@ -46,18 +48,22 @@ module HasLocalizationTable
|
|
46
48
|
|
47
49
|
# Collect all localizations for the object
|
48
50
|
def create_has_many_association
|
49
|
-
self.has_many localization_association_name, localization_table_options.except(
|
51
|
+
self.has_many localization_association_name, localization_table_options.except(*RESERVED_KEYS).reverse_merge(autosave: true) do
|
50
52
|
def for_locale(locale)
|
51
53
|
# where(HasLocalizationTable.locale_foreign_key => locale).first
|
52
54
|
select{ |s| s.send(HasLocalizationTable.locale_foreign_key) == locale }.first
|
53
55
|
end
|
54
56
|
end
|
57
|
+
|
58
|
+
if localization_table_options.fetch(:include, false)
|
59
|
+
self.default_scope -> { includes(localization_association_name) }
|
60
|
+
end
|
55
61
|
end
|
56
62
|
|
57
63
|
public
|
58
64
|
|
59
65
|
module InstanceMethods
|
60
|
-
# Add localization objects for any available locale that doesn't have one
|
66
|
+
# Add localization objects for any available locale that doesn't have one
|
61
67
|
def build_missing_localizations!
|
62
68
|
return unless HasLocalizationTable.all_locales.any?
|
63
69
|
|
@@ -66,11 +72,11 @@ module HasLocalizationTable
|
|
66
72
|
unless localization_association.detect{ |str| str.send(HasLocalizationTable.locale_foreign_key) == locale.id }
|
67
73
|
localization_association.build(HasLocalizationTable.locale_foreign_key => locale.id)
|
68
74
|
end
|
69
|
-
|
75
|
+
|
70
76
|
localization_association.sort_by!{ |l| locale_ids.index(l.send(HasLocalizationTable.locale_foreign_key)) || 0 }
|
71
77
|
end
|
72
78
|
end
|
73
|
-
|
79
|
+
|
74
80
|
# Remove localization objects that are not filled in
|
75
81
|
def reject_empty_localizations!
|
76
82
|
localization_association.reject! { |l| !l.persisted? and localized_attributes.all?{ |attr| l.send(attr).blank? } }
|
@@ -78,4 +84,4 @@ module HasLocalizationTable
|
|
78
84
|
end
|
79
85
|
end
|
80
86
|
end
|
81
|
-
end
|
87
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HasLocalizationTable do
|
4
|
+
before do
|
5
|
+
# Configure HLT
|
6
|
+
HasLocalizationTable.configure do |c|
|
7
|
+
c.primary_locale = Locale.first
|
8
|
+
c.current_locale = Locale.first
|
9
|
+
c.all_locales = Locale.all
|
10
|
+
end
|
11
|
+
|
12
|
+
Object.send(:remove_const, :Article) rescue nil
|
13
|
+
Article = Class.new(ActiveRecord::Base)
|
14
|
+
Article.has_localization_table
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:article) { Article.create!(name: "Test", description: "Description") }
|
18
|
+
|
19
|
+
it 'should initialize the localizations association on initialize' do
|
20
|
+
a = Article.new
|
21
|
+
a.localizations.wont_be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should initialize the localizations association on initialize for an existing object' do
|
25
|
+
a = Article.find(article.id)
|
26
|
+
a.localizations.wont_be_empty
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should not initialize the association on initialize if initialize: false is given in config' do
|
30
|
+
Article.has_localization_table initialize: false
|
31
|
+
a = Article.new
|
32
|
+
a.localizations.must_be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should not initialize the association for an existing object if initialize: false is given in config' do
|
36
|
+
Article.has_localization_table initialize: false
|
37
|
+
a = Article.find(article.id)
|
38
|
+
refute(a.localizations.loaded?)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should not initialize the association on initialize if include: true is given in config' do
|
42
|
+
Article.has_localization_table include: true
|
43
|
+
a = Article.new
|
44
|
+
a.localizations.must_be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should load associations if include: true is given' do
|
48
|
+
Article.has_localization_table include: true
|
49
|
+
assert Article.find(article.id).localizations.loaded?
|
50
|
+
end
|
51
|
+
end
|
@@ -5,7 +5,7 @@ describe HasLocalizationTable do
|
|
5
5
|
before do
|
6
6
|
# Configure HLT
|
7
7
|
HasLocalizationTable.configure do |c|
|
8
|
-
c.primary_locale = Locale.first
|
8
|
+
c.primary_locale = Locale.first
|
9
9
|
c.current_locale = Locale.first
|
10
10
|
c.all_locales = Locale.all
|
11
11
|
end
|
@@ -91,4 +91,26 @@ describe HasLocalizationTable do
|
|
91
91
|
conditions.call.must_equal "article_localizations.locale_id = 3"
|
92
92
|
end
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
|
+
it 'should add a default scope if include: true is given' do
|
96
|
+
Article.stub :localization_table_options, { include: true } do
|
97
|
+
Article.default_scopes.must_be_empty
|
98
|
+
Article.send(:extend, HasLocalizationTable::ActiveRecord::Relation)
|
99
|
+
Article.default_scopes.size.must_equal(1)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should not add a default scope if include: false is given' do
|
104
|
+
Article.stub :localization_table_options, { include: false } do
|
105
|
+
Article.send(:extend, HasLocalizationTable::ActiveRecord::Relation)
|
106
|
+
Article.default_scopes.must_be_empty
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'should not add a default scope if include: is not given' do
|
111
|
+
Article.stub :localization_table_options, { } do
|
112
|
+
Article.send(:extend, HasLocalizationTable::ActiveRecord::Relation)
|
113
|
+
Article.default_scopes.must_be_empty
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: has_localization_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Vandersluis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
type: :runtime
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- lib/has_localization_table/config.rb
|
107
107
|
- lib/has_localization_table/version.rb
|
108
108
|
- spec/active_record/attributes_spec.rb
|
109
|
+
- spec/active_record/callbacks_spec.rb
|
109
110
|
- spec/active_record/finder_method_spec.rb
|
110
111
|
- spec/active_record/ordered_by_spec.rb
|
111
112
|
- spec/active_record/relation_spec.rb
|
@@ -140,6 +141,7 @@ summary: Sets up associations and attribute methods for AR models that have a re
|
|
140
141
|
table to contain user-created data in multiple languages.
|
141
142
|
test_files:
|
142
143
|
- spec/active_record/attributes_spec.rb
|
144
|
+
- spec/active_record/callbacks_spec.rb
|
143
145
|
- spec/active_record/finder_method_spec.rb
|
144
146
|
- spec/active_record/ordered_by_spec.rb
|
145
147
|
- spec/active_record/relation_spec.rb
|