mobility 0.0.1 → 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.
- checksums.yaml +4 -4
- data/Gemfile +19 -0
- data/Gemfile.lock +153 -0
- data/Guardfile +70 -0
- data/README.md +603 -13
- data/Rakefile +42 -0
- data/lib/generators/mobility/install_generator.rb +45 -0
- data/lib/generators/mobility/templates/create_string_translations.rb +15 -0
- data/lib/generators/mobility/templates/create_text_translations.rb +15 -0
- data/lib/mobility.rb +203 -2
- data/lib/mobility/active_model.rb +6 -0
- data/lib/mobility/active_model/attribute_methods.rb +27 -0
- data/lib/mobility/active_model/backend_resetter.rb +26 -0
- data/lib/mobility/active_record.rb +39 -0
- data/lib/mobility/active_record/backend_resetter.rb +26 -0
- data/lib/mobility/active_record/model_translation.rb +14 -0
- data/lib/mobility/active_record/string_translation.rb +7 -0
- data/lib/mobility/active_record/text_translation.rb +7 -0
- data/lib/mobility/active_record/translation.rb +14 -0
- data/lib/mobility/attributes.rb +210 -0
- data/lib/mobility/backend.rb +152 -0
- data/lib/mobility/backend/active_model.rb +7 -0
- data/lib/mobility/backend/active_model/dirty.rb +84 -0
- data/lib/mobility/backend/active_record.rb +13 -0
- data/lib/mobility/backend/active_record/column.rb +52 -0
- data/lib/mobility/backend/active_record/column/query_methods.rb +40 -0
- data/lib/mobility/backend/active_record/hash_valued.rb +58 -0
- data/lib/mobility/backend/active_record/hstore.rb +36 -0
- data/lib/mobility/backend/active_record/hstore/query_methods.rb +53 -0
- data/lib/mobility/backend/active_record/jsonb.rb +43 -0
- data/lib/mobility/backend/active_record/jsonb/query_methods.rb +53 -0
- data/lib/mobility/backend/active_record/key_value.rb +126 -0
- data/lib/mobility/backend/active_record/key_value/query_methods.rb +63 -0
- data/lib/mobility/backend/active_record/query_methods.rb +36 -0
- data/lib/mobility/backend/active_record/serialized.rb +93 -0
- data/lib/mobility/backend/active_record/serialized/query_methods.rb +32 -0
- data/lib/mobility/backend/active_record/table.rb +197 -0
- data/lib/mobility/backend/active_record/table/query_methods.rb +91 -0
- data/lib/mobility/backend/cache.rb +110 -0
- data/lib/mobility/backend/column.rb +52 -0
- data/lib/mobility/backend/dirty.rb +28 -0
- data/lib/mobility/backend/fallbacks.rb +89 -0
- data/lib/mobility/backend/hstore.rb +21 -0
- data/lib/mobility/backend/jsonb.rb +21 -0
- data/lib/mobility/backend/key_value.rb +71 -0
- data/lib/mobility/backend/null.rb +24 -0
- data/lib/mobility/backend/orm_delegator.rb +33 -0
- data/lib/mobility/backend/sequel.rb +14 -0
- data/lib/mobility/backend/sequel/column.rb +40 -0
- data/lib/mobility/backend/sequel/column/query_methods.rb +24 -0
- data/lib/mobility/backend/sequel/dirty.rb +54 -0
- data/lib/mobility/backend/sequel/hash_valued.rb +51 -0
- data/lib/mobility/backend/sequel/hstore.rb +36 -0
- data/lib/mobility/backend/sequel/hstore/query_methods.rb +42 -0
- data/lib/mobility/backend/sequel/jsonb.rb +43 -0
- data/lib/mobility/backend/sequel/jsonb/query_methods.rb +42 -0
- data/lib/mobility/backend/sequel/key_value.rb +139 -0
- data/lib/mobility/backend/sequel/key_value/query_methods.rb +48 -0
- data/lib/mobility/backend/sequel/query_methods.rb +22 -0
- data/lib/mobility/backend/sequel/serialized.rb +133 -0
- data/lib/mobility/backend/sequel/serialized/query_methods.rb +20 -0
- data/lib/mobility/backend/sequel/table.rb +149 -0
- data/lib/mobility/backend/sequel/table/query_methods.rb +48 -0
- data/lib/mobility/backend/serialized.rb +53 -0
- data/lib/mobility/backend/table.rb +93 -0
- data/lib/mobility/backend_resetter.rb +44 -0
- data/lib/mobility/configuration.rb +31 -0
- data/lib/mobility/core_ext/nil.rb +10 -0
- data/lib/mobility/core_ext/object.rb +19 -0
- data/lib/mobility/core_ext/string.rb +16 -0
- data/lib/mobility/instance_methods.rb +34 -0
- data/lib/mobility/orm.rb +4 -0
- data/lib/mobility/sequel.rb +26 -0
- data/lib/mobility/sequel/backend_resetter.rb +26 -0
- data/lib/mobility/sequel/column_changes.rb +29 -0
- data/lib/mobility/sequel/model_translation.rb +20 -0
- data/lib/mobility/sequel/string_translation.rb +7 -0
- data/lib/mobility/sequel/text_translation.rb +7 -0
- data/lib/mobility/sequel/translation.rb +53 -0
- data/lib/mobility/translates.rb +75 -0
- data/lib/mobility/wrapper.rb +31 -0
- metadata +152 -12
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/mobility.gemspec +0 -32
@@ -0,0 +1,14 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
module Sequel
|
4
|
+
autoload :Column, 'mobility/backend/sequel/column'
|
5
|
+
autoload :Dirty, 'mobility/backend/sequel/dirty'
|
6
|
+
autoload :Hstore, 'mobility/backend/sequel/hstore'
|
7
|
+
autoload :Jsonb, 'mobility/backend/sequel/jsonb'
|
8
|
+
autoload :KeyValue, 'mobility/backend/sequel/key_value'
|
9
|
+
autoload :Serialized, 'mobility/backend/sequel/serialized'
|
10
|
+
autoload :Table, 'mobility/backend/sequel/table'
|
11
|
+
autoload :QueryMethods, 'mobility/backend/sequel/query_methods'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Implements the {Mobility::Backend::Column} backend for Sequel models.
|
6
|
+
|
7
|
+
@note This backend disables the +accessor_locales+ option, which would
|
8
|
+
otherwise interfere with column methods.
|
9
|
+
=end
|
10
|
+
class Sequel::Column
|
11
|
+
include Backend
|
12
|
+
include Mobility::Backend::Column
|
13
|
+
|
14
|
+
autoload :QueryMethods, 'mobility/backend/sequel/column/query_methods'
|
15
|
+
|
16
|
+
# @!group Backend Accessors
|
17
|
+
# @!macro backend_reader
|
18
|
+
# @!method read(locale, **options)
|
19
|
+
|
20
|
+
# @!group Backend Accessors
|
21
|
+
# @!macro backend_writer
|
22
|
+
# @!method write(locale, value, **options)
|
23
|
+
|
24
|
+
# @!group Backend Configuration
|
25
|
+
def self.configure!(options)
|
26
|
+
options[:locale_accessors] = false
|
27
|
+
end
|
28
|
+
# @!endgroup
|
29
|
+
|
30
|
+
setup do |attributes, options|
|
31
|
+
extension = Module.new do
|
32
|
+
define_method :i18n do
|
33
|
+
@mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
extend extension
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
class Sequel::Column::QueryMethods < Backend::Sequel::QueryMethods
|
4
|
+
def initialize(attributes, **options)
|
5
|
+
super
|
6
|
+
attributes_extractor = @attributes_extractor
|
7
|
+
|
8
|
+
define_method :_filter_or_exclude do |invert, clause, cond, &block|
|
9
|
+
if keys = attributes_extractor.call(cond)
|
10
|
+
cond = cond.dup
|
11
|
+
keys.each { |attr| cond[Column.column_name_for(attr)] = cond.delete(attr) }
|
12
|
+
end
|
13
|
+
super(invert, clause, cond, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
attributes.each do |attribute|
|
17
|
+
define_method :"first_by_#{attribute}" do |value|
|
18
|
+
where(attribute.to_sym => value).first
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Dirty tracking for Sequel models which use the +Sequel::Plugins::Dirty+ plugin.
|
6
|
+
|
7
|
+
@see http://sequel.jeremyevans.net/rdoc-plugins/index.html Sequel dirty plugin
|
8
|
+
|
9
|
+
=end
|
10
|
+
module Sequel::Dirty
|
11
|
+
# @!group Backend Accessors
|
12
|
+
# @!macro backend_writer
|
13
|
+
def write(locale, value, **options)
|
14
|
+
locale_accessor = "#{attribute}_#{locale}".to_sym
|
15
|
+
if model.column_changes.has_key?(locale_accessor) && model.initial_values[locale_accessor] == value
|
16
|
+
super
|
17
|
+
[model.changed_columns, model.initial_values].each { |h| h.delete(locale_accessor) }
|
18
|
+
else
|
19
|
+
model.will_change_column("#{attribute}_#{locale}".to_sym)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
# @!endgroup
|
24
|
+
|
25
|
+
# @param [Class] backend_class Class of backend
|
26
|
+
def self.included(backend_class)
|
27
|
+
backend_class.extend(ClassMethods)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Adds hook after {Backend::Setup#setup_model} to add dirty-tracking
|
31
|
+
# methods for translated attributes onto model class.
|
32
|
+
module ClassMethods
|
33
|
+
# (see Mobility::Backend::Setup#setup_model)
|
34
|
+
def setup_model(model_class, attributes, **options)
|
35
|
+
super
|
36
|
+
model_class.class_eval do
|
37
|
+
mod = Module.new do
|
38
|
+
%w[initial_value column_change column_changed? reset_column].each do |method_name|
|
39
|
+
define_method method_name do |column|
|
40
|
+
if attributes.map(&:to_sym).include?(column)
|
41
|
+
super("#{column}_#{Mobility.locale}".to_sym)
|
42
|
+
else
|
43
|
+
super(column)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
include mod
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Internal class used by Sequel backends that store values as a hash.
|
6
|
+
|
7
|
+
=end
|
8
|
+
class Sequel::HashValued
|
9
|
+
include Backend
|
10
|
+
|
11
|
+
# @!macro backend_reader
|
12
|
+
def read(locale, **options)
|
13
|
+
translations[locale.to_s]
|
14
|
+
end
|
15
|
+
|
16
|
+
# @!macro backend_writer
|
17
|
+
def write(locale, value, **options)
|
18
|
+
translations[locale.to_s] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
# @!group Cache Methods
|
22
|
+
def translations
|
23
|
+
model.send("#{attribute}_before_mobility")
|
24
|
+
end
|
25
|
+
alias_method :new_cache, :translations
|
26
|
+
|
27
|
+
# @return [Boolean]
|
28
|
+
def write_to_cache?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
# @!endgroup
|
32
|
+
|
33
|
+
setup do |attributes, options|
|
34
|
+
method_overrides = Module.new do
|
35
|
+
define_method :initialize_set do |values|
|
36
|
+
attributes.each { |attribute| send(:"#{attribute}_before_mobility=", {}) }
|
37
|
+
super(values)
|
38
|
+
end
|
39
|
+
define_method :before_validation do
|
40
|
+
attributes.each do |attribute|
|
41
|
+
send("#{attribute}_before_mobility").delete_if { |_, v| v.blank? }
|
42
|
+
end
|
43
|
+
super()
|
44
|
+
end
|
45
|
+
end
|
46
|
+
include method_overrides
|
47
|
+
include Mobility::Sequel::ColumnChanges.new(attributes)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'mobility/backend/sequel/hash_valued'
|
2
|
+
|
3
|
+
module Mobility
|
4
|
+
module Backend
|
5
|
+
=begin
|
6
|
+
|
7
|
+
Implements the {Mobility::Backend::Hstore} backend for Sequel models.
|
8
|
+
|
9
|
+
@see Mobility::Backend::Sequel::HashValued
|
10
|
+
|
11
|
+
=end
|
12
|
+
class Sequel::Hstore < Sequel::HashValued
|
13
|
+
autoload :QueryMethods, 'mobility/backend/sequel/hstore/query_methods'
|
14
|
+
|
15
|
+
# @!group Backend Accessors
|
16
|
+
# @!macro backend_reader
|
17
|
+
# @!method read(locale, **options)
|
18
|
+
|
19
|
+
# @!group Backend Accessors
|
20
|
+
# @!macro backend_writer
|
21
|
+
def write(locale, value, **options)
|
22
|
+
translations[locale.to_s] = value && value.to_s
|
23
|
+
end
|
24
|
+
# @!endgroup
|
25
|
+
|
26
|
+
setup do |attributes, options|
|
27
|
+
extension = Module.new do
|
28
|
+
define_method :i18n do
|
29
|
+
@mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
extend extension
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Sequel.extension :pg_hstore, :pg_hstore_ops
|
2
|
+
|
3
|
+
module Mobility
|
4
|
+
module Backend
|
5
|
+
class Sequel::Hstore::QueryMethods < Sequel::QueryMethods
|
6
|
+
def initialize(attributes, **options)
|
7
|
+
super
|
8
|
+
attributes_extractor = @attributes_extractor
|
9
|
+
|
10
|
+
define_method :_filter_or_exclude do |invert, clause, *conds, &block|
|
11
|
+
if (clause == :where) && i18n_keys = attributes_extractor.call(conds.first)
|
12
|
+
locale = Mobility.locale.to_s
|
13
|
+
table_name = model.table_name
|
14
|
+
cond = conds.first
|
15
|
+
|
16
|
+
i18n_query = i18n_keys.inject(::Sequel.expr(!invert)) do |expr, attr|
|
17
|
+
value = cond.delete(attr)
|
18
|
+
attr_hstore = ::Sequel.hstore_op(attr)
|
19
|
+
contains_value = attr_hstore.contains({ locale => value.to_s })
|
20
|
+
has_key = attr_hstore.has_key?(locale)
|
21
|
+
if invert
|
22
|
+
expr.|(has_key & ~contains_value)
|
23
|
+
else
|
24
|
+
expr.&(value.nil? ? ~has_key : contains_value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
super(invert, clause, *conds, &block).where(i18n_query)
|
28
|
+
else
|
29
|
+
super(invert, clause, *conds, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attributes.each do |attribute|
|
34
|
+
define_method :"first_by_#{attribute}" do |value|
|
35
|
+
where(::Sequel.hstore(attribute.to_sym).contains(::Sequel.hstore({ Mobility.locale.to_s => value }))).
|
36
|
+
select_all(model.table_name).first
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'mobility/backend/sequel/hash_valued'
|
2
|
+
|
3
|
+
module Mobility
|
4
|
+
module Backend
|
5
|
+
=begin
|
6
|
+
|
7
|
+
Implements the {Mobility::Backend::Jsonb} backend for Sequel models.
|
8
|
+
|
9
|
+
@see Mobility::Backend::Sequel::HashValued
|
10
|
+
|
11
|
+
=end
|
12
|
+
class Sequel::Jsonb < Sequel::HashValued
|
13
|
+
autoload :QueryMethods, 'mobility/backend/sequel/jsonb/query_methods'
|
14
|
+
|
15
|
+
# @!group Backend Accessors
|
16
|
+
#
|
17
|
+
# @note Translation may be string, integer or boolean-valued since
|
18
|
+
# value is stored on a JSON hash.
|
19
|
+
# @param [Symbol] locale Locale to read
|
20
|
+
# @param [Hash] options
|
21
|
+
# @return [String,Integer,Boolean] Value of translation
|
22
|
+
# @!method read(locale, **options)
|
23
|
+
|
24
|
+
# @!group Backend Accessors
|
25
|
+
# @note Translation may be string, integer or boolean-valued since
|
26
|
+
# value is stored on a JSON hash.
|
27
|
+
# @param [Symbol] locale Locale to write
|
28
|
+
# @param [String,Integer,Boolean] value Value to write
|
29
|
+
# @param [Hash] options
|
30
|
+
# @return [String,Integer,Boolean] Updated value
|
31
|
+
# @!method write(locale, value, **options)
|
32
|
+
|
33
|
+
setup do |attributes, options|
|
34
|
+
extension = Module.new do
|
35
|
+
define_method :i18n do
|
36
|
+
@mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
extend extension
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Sequel.extension :pg_json, :pg_json_ops
|
2
|
+
|
3
|
+
module Mobility
|
4
|
+
module Backend
|
5
|
+
class Sequel::Jsonb::QueryMethods < Sequel::QueryMethods
|
6
|
+
def initialize(attributes, **options)
|
7
|
+
super
|
8
|
+
attributes_extractor = @attributes_extractor
|
9
|
+
|
10
|
+
define_method :_filter_or_exclude do |invert, clause, *conds, &block|
|
11
|
+
if (clause == :where) && i18n_keys = attributes_extractor.call(conds.first)
|
12
|
+
locale = Mobility.locale.to_s
|
13
|
+
table_name = model.table_name
|
14
|
+
cond = conds.first
|
15
|
+
|
16
|
+
i18n_query = i18n_keys.inject(::Sequel.expr(!invert)) do |expr, attr|
|
17
|
+
value = cond.delete(attr)
|
18
|
+
attr_jsonb = ::Sequel.pg_jsonb_op(attr)
|
19
|
+
contains_value = attr_jsonb.contains({ locale => value })
|
20
|
+
has_key = attr_jsonb.has_key?(locale)
|
21
|
+
if invert
|
22
|
+
expr.|(has_key & ~contains_value)
|
23
|
+
else
|
24
|
+
expr.&(value.nil? ? ~has_key : contains_value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
super(invert, clause, *conds, &block).where(i18n_query)
|
28
|
+
else
|
29
|
+
super(invert, clause, *conds, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attributes.each do |attribute|
|
34
|
+
define_method :"first_by_#{attribute}" do |value|
|
35
|
+
where(::Sequel.pg_jsonb_op(attribute).contains({ Mobility.locale => value })).
|
36
|
+
select_all(model.table_name).first
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Mobility
|
2
|
+
module Backend
|
3
|
+
=begin
|
4
|
+
|
5
|
+
Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
|
6
|
+
|
7
|
+
@note This backend requires the cache to be enabled in order to track
|
8
|
+
and store changed translations, since Sequel does not support +build+-type
|
9
|
+
methods on associations like ActiveRecord.
|
10
|
+
|
11
|
+
=end
|
12
|
+
class Sequel::KeyValue
|
13
|
+
include Backend
|
14
|
+
|
15
|
+
autoload :QueryMethods, 'mobility/backend/sequel/key_value/query_methods'
|
16
|
+
|
17
|
+
# @return [Symbol] name of the association
|
18
|
+
attr_reader :association_name
|
19
|
+
|
20
|
+
# @return [Class] translation model class
|
21
|
+
attr_reader :class_name
|
22
|
+
|
23
|
+
# @!macro backend_constructor
|
24
|
+
# @option options [Symbol] association_name Name of association
|
25
|
+
# @option options [Class] class_name Translation model class
|
26
|
+
def initialize(model, attribute, **options)
|
27
|
+
super
|
28
|
+
@association_name = options[:association_name]
|
29
|
+
@class_name = options[:class_name]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!group Backend Accessors
|
33
|
+
# @!macro backend_reader
|
34
|
+
def read(locale, **options)
|
35
|
+
translation_for(locale).value
|
36
|
+
end
|
37
|
+
|
38
|
+
# @!macro backend_writer
|
39
|
+
def write(locale, value, **options)
|
40
|
+
translation_for(locale).tap { |t| t.value = value }.value
|
41
|
+
end
|
42
|
+
# @!endgroup
|
43
|
+
|
44
|
+
# @!group Backend Configuration
|
45
|
+
# @option options [Symbol] type (:text) Column type to use
|
46
|
+
# @option options [Symbol] associaiton_name (:mobility_text_translations) Name of association method
|
47
|
+
# @option options [Symbol] class_name ({Mobility::Sequel::TextTranslation}) Translation class
|
48
|
+
# @raise [CacheRequired] if cache is disabled
|
49
|
+
# @raise [ArgumentError] if type is not either :text or :string
|
50
|
+
def self.configure!(options)
|
51
|
+
raise CacheRequired, "Cache required for Sequel::KeyValue backend" if options[:cache] == false
|
52
|
+
options[:type] ||= :text
|
53
|
+
case type = options[:type].to_sym
|
54
|
+
when :text, :string
|
55
|
+
options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation")
|
56
|
+
else
|
57
|
+
raise ArgumentError, "type must be one of: [text, string]"
|
58
|
+
end
|
59
|
+
options[:class_name] = options[:class_name].constantize if options[:class_name].is_a?(String)
|
60
|
+
options[:association_name] ||= options[:class_name].table_name.to_sym
|
61
|
+
%i[type association_name].each { |key| options[key] = options[key].to_sym }
|
62
|
+
end
|
63
|
+
# @!endgroup
|
64
|
+
|
65
|
+
setup do |attributes, options|
|
66
|
+
association_name = options[:association_name]
|
67
|
+
translations_class = options[:class_name]
|
68
|
+
|
69
|
+
attrs_method_name = :"#{association_name}_attributes"
|
70
|
+
association_attributes = (instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
71
|
+
instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
72
|
+
|
73
|
+
one_to_many association_name,
|
74
|
+
reciprocal: :translatable,
|
75
|
+
key: :translatable_id,
|
76
|
+
reciprocal_type: :one_to_many,
|
77
|
+
conditions: { translatable_type: self.to_s, key: association_attributes },
|
78
|
+
adder: proc { |translation| translation.update(translatable_id: pk, translatable_type: self.class.to_s) },
|
79
|
+
remover: proc { |translation| translation.update(translatable_id: nil, translatable_type: nil) },
|
80
|
+
clearer: proc { send(:"#{association_name}_dataset").update(translatable_id: nil, translatable_type: nil) },
|
81
|
+
class: translations_class
|
82
|
+
|
83
|
+
plugin :association_dependencies, association_name => :destroy
|
84
|
+
|
85
|
+
callback_methods = Module.new do
|
86
|
+
define_method :before_save do
|
87
|
+
super()
|
88
|
+
send(association_name).select { |t| attributes.include?(t.key) && t.value.blank? }.each(&:destroy)
|
89
|
+
end
|
90
|
+
define_method :after_save do
|
91
|
+
super()
|
92
|
+
attributes.each { |attribute| mobility_backend_for(attribute).save_translations }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
include callback_methods
|
96
|
+
|
97
|
+
extension = Module.new do
|
98
|
+
define_method :i18n do
|
99
|
+
@mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
extend extension
|
103
|
+
|
104
|
+
include Mobility::Sequel::ColumnChanges.new(attributes)
|
105
|
+
end
|
106
|
+
|
107
|
+
# @!group Cache Methods
|
108
|
+
# @return [KeyValue::TranslationsCache]
|
109
|
+
def new_cache
|
110
|
+
KeyValue::TranslationsCache.new(self)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Boolean]
|
114
|
+
def write_to_cache?
|
115
|
+
true
|
116
|
+
end
|
117
|
+
# @!endgroup
|
118
|
+
|
119
|
+
# Returns translation for a given locale, or initializes one if none is present.
|
120
|
+
# @param [Symbol] locale
|
121
|
+
# @return [Mobility::Sequel::TextTranslation,Mobility::Sequel::StringTranslation]
|
122
|
+
def translation_for(locale)
|
123
|
+
translation = model.send(association_name).find { |t| t.key == attribute && t.locale == locale.to_s }
|
124
|
+
translation ||= class_name.new(locale: locale, key: attribute)
|
125
|
+
translation
|
126
|
+
end
|
127
|
+
|
128
|
+
# Saves translation which have been built and which have non-blank values.
|
129
|
+
def save_translations
|
130
|
+
cache.each_translation do |translation|
|
131
|
+
next unless translation.value.present?
|
132
|
+
translation.id ? translation.save : model.send("add_#{association_name.to_s.singularize}", translation)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class CacheRequired < ::StandardError; end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|