mobility 0.8.8 → 1.0.0.alpha
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +56 -0
- data/Gemfile +52 -16
- data/Gemfile.lock +113 -52
- data/Guardfile +23 -1
- data/README.md +184 -92
- data/Rakefile +6 -4
- data/lib/mobility.rb +40 -166
- data/lib/mobility/active_record/translation.rb +1 -1
- data/lib/mobility/arel/nodes/pg_ops.rb +1 -1
- data/lib/mobility/backend.rb +19 -41
- data/lib/mobility/backends.rb +20 -0
- data/lib/mobility/backends/active_record.rb +4 -0
- data/lib/mobility/backends/active_record/column.rb +2 -0
- data/lib/mobility/backends/active_record/container.rb +4 -2
- data/lib/mobility/backends/active_record/hstore.rb +2 -0
- data/lib/mobility/backends/active_record/json.rb +2 -0
- data/lib/mobility/backends/active_record/jsonb.rb +2 -0
- data/lib/mobility/backends/active_record/key_value.rb +5 -3
- data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
- data/lib/mobility/backends/active_record/serialized.rb +2 -0
- data/lib/mobility/backends/active_record/table.rb +5 -3
- data/lib/mobility/backends/column.rb +0 -6
- data/lib/mobility/backends/container.rb +2 -1
- data/lib/mobility/backends/hash.rb +39 -0
- data/lib/mobility/backends/hstore.rb +0 -1
- data/lib/mobility/backends/json.rb +0 -1
- data/lib/mobility/backends/jsonb.rb +0 -1
- data/lib/mobility/backends/key_value.rb +22 -14
- data/lib/mobility/backends/null.rb +2 -0
- data/lib/mobility/backends/sequel.rb +3 -0
- data/lib/mobility/backends/sequel/column.rb +2 -0
- data/lib/mobility/backends/sequel/container.rb +3 -1
- data/lib/mobility/backends/sequel/hstore.rb +2 -0
- data/lib/mobility/backends/sequel/json.rb +2 -0
- data/lib/mobility/backends/sequel/jsonb.rb +3 -1
- data/lib/mobility/backends/sequel/key_value.rb +8 -6
- data/lib/mobility/backends/sequel/serialized.rb +2 -0
- data/lib/mobility/backends/sequel/table.rb +5 -2
- data/lib/mobility/backends/serialized.rb +1 -3
- data/lib/mobility/backends/table.rb +14 -6
- data/lib/mobility/pluggable.rb +36 -0
- data/lib/mobility/plugin.rb +260 -0
- data/lib/mobility/plugins.rb +26 -25
- data/lib/mobility/plugins/active_model.rb +17 -0
- data/lib/mobility/plugins/active_model/cache.rb +26 -0
- data/lib/mobility/plugins/active_model/dirty.rb +310 -54
- data/lib/mobility/plugins/active_record.rb +34 -0
- data/lib/mobility/plugins/active_record/backend.rb +25 -0
- data/lib/mobility/plugins/active_record/cache.rb +28 -0
- data/lib/mobility/plugins/active_record/dirty.rb +72 -101
- data/lib/mobility/plugins/active_record/query.rb +48 -34
- data/lib/mobility/plugins/active_record/uniqueness_validation.rb +60 -0
- data/lib/mobility/plugins/attribute_methods.rb +28 -20
- data/lib/mobility/plugins/attributes.rb +70 -0
- data/lib/mobility/plugins/backend.rb +138 -0
- data/lib/mobility/plugins/backend_reader.rb +34 -0
- data/lib/mobility/plugins/cache.rb +59 -24
- data/lib/mobility/plugins/default.rb +22 -17
- data/lib/mobility/plugins/dirty.rb +12 -33
- data/lib/mobility/plugins/fallbacks.rb +51 -43
- data/lib/mobility/plugins/fallthrough_accessors.rb +26 -25
- data/lib/mobility/plugins/locale_accessors.rb +25 -35
- data/lib/mobility/plugins/presence.rb +28 -21
- data/lib/mobility/plugins/query.rb +8 -17
- data/lib/mobility/plugins/reader.rb +50 -0
- data/lib/mobility/plugins/sequel.rb +34 -0
- data/lib/mobility/plugins/sequel/backend.rb +25 -0
- data/lib/mobility/plugins/sequel/cache.rb +24 -0
- data/lib/mobility/plugins/sequel/dirty.rb +45 -32
- data/lib/mobility/plugins/sequel/query.rb +21 -6
- data/lib/mobility/plugins/writer.rb +44 -0
- data/lib/mobility/translations.rb +95 -0
- data/lib/mobility/version.rb +12 -1
- data/lib/rails/generators/mobility/templates/initializer.rb +95 -77
- metadata +51 -51
- metadata.gz.sig +0 -0
- data/lib/mobility/active_model.rb +0 -4
- data/lib/mobility/active_model/backend_resetter.rb +0 -26
- data/lib/mobility/active_record.rb +0 -23
- data/lib/mobility/active_record/backend_resetter.rb +0 -26
- data/lib/mobility/active_record/uniqueness_validator.rb +0 -60
- data/lib/mobility/attributes.rb +0 -324
- data/lib/mobility/backend/orm_delegator.rb +0 -44
- data/lib/mobility/backend_resetter.rb +0 -50
- data/lib/mobility/configuration.rb +0 -138
- data/lib/mobility/fallbacks.rb +0 -28
- data/lib/mobility/interface.rb +0 -0
- data/lib/mobility/loaded.rb +0 -4
- data/lib/mobility/plugins/active_record/attribute_methods.rb +0 -38
- data/lib/mobility/plugins/cache/translation_cacher.rb +0 -40
- data/lib/mobility/sequel.rb +0 -9
- data/lib/mobility/sequel/backend_resetter.rb +0 -23
- data/lib/mobility/translates.rb +0 -73
@@ -12,30 +12,38 @@ attributes only.
|
|
12
12
|
|
13
13
|
=end
|
14
14
|
module AttributeMethods
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
extend Plugin
|
16
|
+
|
17
|
+
default true
|
18
|
+
|
19
|
+
initialize_hook do |*names|
|
20
|
+
include InstanceMethods
|
21
|
+
|
22
|
+
define_method :translated_attributes do
|
23
|
+
super().merge(names.inject({}) do |attributes, name|
|
24
|
+
attributes.merge(name.to_s => send(name))
|
25
|
+
end)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Applies attribute_methods plugin for a given option value.
|
30
|
+
included_hook do
|
31
|
+
if options[:attribute_methods]
|
32
|
+
define_method :untranslated_attributes, ::ActiveRecord::Base.instance_method(:attributes)
|
24
33
|
end
|
34
|
+
end
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
else
|
34
|
-
raise ArgumentError, "#{model_class} does not support AttributeMethods plugin."
|
35
|
-
end
|
36
|
-
model_class.include module_builder.new(*attribute_names)
|
36
|
+
module InstanceMethods
|
37
|
+
def translated_attributes
|
38
|
+
{}
|
39
|
+
end
|
40
|
+
|
41
|
+
def attributes
|
42
|
+
super.merge(translated_attributes)
|
37
43
|
end
|
38
44
|
end
|
39
45
|
end
|
46
|
+
|
47
|
+
register_plugin(:attribute_methods, AttributeMethods)
|
40
48
|
end
|
41
49
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
module Mobility
|
3
|
+
module Plugins
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Takes arguments, converts them to strings, and stores in an array +@names+,
|
7
|
+
made available with an +attr_reader+. Also provides some convenience methods
|
8
|
+
for aggregating attributes.
|
9
|
+
|
10
|
+
=end
|
11
|
+
module Attributes
|
12
|
+
extend Plugin
|
13
|
+
|
14
|
+
# Attribute names for which accessors will be defined
|
15
|
+
# @return [Array<String>] Array of names
|
16
|
+
attr_reader :names
|
17
|
+
|
18
|
+
initialize_hook do |*names|
|
19
|
+
@names = names.map(&:to_s).freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
# Yield each attribute name to block
|
23
|
+
# @yieldparam [String] Attribute
|
24
|
+
def each &block
|
25
|
+
names.each(&block)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Show useful information about this module.
|
29
|
+
# @return [String]
|
30
|
+
def inspect
|
31
|
+
"#<Translations @names=#{names.join(", ")}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
included_hook do |klass|
|
35
|
+
klass.extend ClassMethods
|
36
|
+
@names.each { |name| klass.register_mobility_attribute(name) }
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
# Return true if attribute name is translated on this model.
|
41
|
+
# @param [String, Symbol] Attribute name
|
42
|
+
# @return [Boolean]
|
43
|
+
def mobility_attribute?(name)
|
44
|
+
mobility_attributes.include?(name.to_s)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Register a new attribute name. Public, but treat as internal.
|
48
|
+
# @param [String, Symbol] Attribute name
|
49
|
+
def register_mobility_attribute(name)
|
50
|
+
(self.mobility_attributes << name.to_s).uniq!
|
51
|
+
end
|
52
|
+
|
53
|
+
def inherited(klass)
|
54
|
+
super
|
55
|
+
mobility_attributes.each { |name| klass.register_mobility_attribute(name) }
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Return translated attribute names on this model.
|
61
|
+
# @return [Array<String>] Attribute names
|
62
|
+
def mobility_attributes
|
63
|
+
@mobility_attributes ||= []
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
register_plugin(:attributes, Attributes)
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
module Mobility
|
3
|
+
module Plugins
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Plugin for setting up a backend for a set of model attributes. All backend
|
7
|
+
plugins must depend on this.
|
8
|
+
|
9
|
+
Defines:
|
10
|
+
- instance method +mobility_backends+ which returns a hash whose keys are
|
11
|
+
attribute names and values a backend for each attribute.
|
12
|
+
- class method +mobility_backend_class+ which takes an attribute name and
|
13
|
+
returns the backend class for that name.
|
14
|
+
|
15
|
+
=end
|
16
|
+
module Backend
|
17
|
+
extend Plugin
|
18
|
+
|
19
|
+
requires :attributes, include: :before
|
20
|
+
|
21
|
+
# Backend class
|
22
|
+
# @return [Class] Backend class
|
23
|
+
attr_reader :backend_class
|
24
|
+
|
25
|
+
# Backend
|
26
|
+
# @return [Symbol,Class,Class] Name of backend, or backend class
|
27
|
+
attr_reader :backend
|
28
|
+
|
29
|
+
# Backend options
|
30
|
+
# @return [Hash] Options for backend
|
31
|
+
attr_reader :backend_options
|
32
|
+
|
33
|
+
def initialize(*args, **original_options)
|
34
|
+
super
|
35
|
+
return unless Plugins::Backend.dependencies_satisfied?(self.class)
|
36
|
+
|
37
|
+
case options[:backend]
|
38
|
+
when String, Symbol, Class
|
39
|
+
@backend, @backend_options = options[:backend], options
|
40
|
+
when Array
|
41
|
+
@backend, @backend_options = options[:backend]
|
42
|
+
@backend_options = @backend_options.merge(original_options)
|
43
|
+
when NilClass
|
44
|
+
@backend = @backend_options = nil
|
45
|
+
else
|
46
|
+
raise ArgumentError, "backend must be either a backend name, a backend class, or a two-element array"
|
47
|
+
end
|
48
|
+
|
49
|
+
include InstanceMethods
|
50
|
+
end
|
51
|
+
|
52
|
+
# Setup backend class, include modules into model class, include/extend
|
53
|
+
# shared modules and setup model with backend setup block (see
|
54
|
+
# {Mobility::Backend::Setup#setup_model}).
|
55
|
+
def included(klass)
|
56
|
+
super
|
57
|
+
|
58
|
+
klass.extend ClassMethods
|
59
|
+
|
60
|
+
if backend
|
61
|
+
@backend_class = load_backend(backend).
|
62
|
+
build_subclass(klass, backend_options)
|
63
|
+
|
64
|
+
backend_class.setup_model(klass, names)
|
65
|
+
|
66
|
+
@names.each do |name|
|
67
|
+
klass.register_mobility_backend_class(name, @backend_class)
|
68
|
+
end
|
69
|
+
|
70
|
+
backend_class
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Include backend name in inspect string.
|
75
|
+
# @return [String]
|
76
|
+
def inspect
|
77
|
+
"#<Translations (#{backend}) @names=#{names.join(", ")}>"
|
78
|
+
end
|
79
|
+
|
80
|
+
def load_backend(backend)
|
81
|
+
Backends.load_backend(backend)
|
82
|
+
rescue Backends::LoadError => e
|
83
|
+
raise e, "could not find a #{backend} backend. Did you forget to include an ORM plugin like active_record or sequel?"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Override default argument-handling in DSL to store kwargs passed along
|
87
|
+
# with plugin name.
|
88
|
+
def self.configure_default(defaults, key, *args, **kwargs)
|
89
|
+
defaults[key] = [args[0], kwargs] unless args.empty?
|
90
|
+
end
|
91
|
+
|
92
|
+
module InstanceMethods
|
93
|
+
# Return a new backend for an attribute name.
|
94
|
+
# @return [Hash] Hash of attribute names and backend instances
|
95
|
+
# @api private
|
96
|
+
def mobility_backends
|
97
|
+
@mobility_backends ||= ::Hash.new do |hash, name|
|
98
|
+
next hash[name.to_sym] if String === name
|
99
|
+
hash[name] = self.class.mobility_backend_class(name).new(self, name.to_s)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def initialize_dup(other)
|
104
|
+
@mobility_backends = nil
|
105
|
+
super
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
module ClassMethods
|
110
|
+
# Return backend class for a given attribute name.
|
111
|
+
# @param [Symbol,String] Name of attribute
|
112
|
+
# @return [Class] Backend class
|
113
|
+
def mobility_backend_class(name)
|
114
|
+
mobility_backend_classes.fetch(name.to_sym)
|
115
|
+
rescue KeyError
|
116
|
+
raise KeyError, "No backend for: #{name}"
|
117
|
+
end
|
118
|
+
|
119
|
+
def register_mobility_backend_class(name, backend_class)
|
120
|
+
mobility_backend_classes[name.to_sym] = backend_class
|
121
|
+
end
|
122
|
+
|
123
|
+
def inherited(klass)
|
124
|
+
klass.mobility_backend_classes.merge!(@mobility_backend_classes)
|
125
|
+
super
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
def mobility_backend_classes
|
131
|
+
@mobility_backend_classes ||= {}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
register_plugin(:backend, Backend)
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
module Mobility
|
3
|
+
module Plugins
|
4
|
+
=begin
|
5
|
+
|
6
|
+
Defines convenience methods for accessing backends, of the form
|
7
|
+
"<name>_backend". The format for this method can be customized by passing a
|
8
|
+
different format string as the plugin option.
|
9
|
+
|
10
|
+
=end
|
11
|
+
module BackendReader
|
12
|
+
extend Plugin
|
13
|
+
|
14
|
+
default true
|
15
|
+
requires :backend
|
16
|
+
|
17
|
+
initialize_hook do |*names|
|
18
|
+
if backend_reader = options[:backend_reader]
|
19
|
+
backend_reader = "%s_backend" if backend_reader == true
|
20
|
+
|
21
|
+
names.each do |name|
|
22
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
23
|
+
def #{backend_reader % name}
|
24
|
+
mobility_backends[:#{name}]
|
25
|
+
end
|
26
|
+
EOM
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
register_plugin(:backend_reader, BackendReader)
|
33
|
+
end
|
34
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require "mobility/plugins/cache/translation_cacher"
|
3
2
|
|
4
3
|
module Mobility
|
5
4
|
module Plugins
|
@@ -21,34 +20,70 @@ Values are added to the cache in two ways:
|
|
21
20
|
|
22
21
|
=end
|
23
22
|
module Cache
|
23
|
+
extend Plugin
|
24
|
+
|
25
|
+
default true
|
26
|
+
requires :backend, include: :before
|
27
|
+
|
24
28
|
# Applies cache plugin to attributes.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
if option
|
29
|
-
backend_class = attributes.backend_class
|
30
|
-
backend_class.include(self) unless backend_class.apply_plugin(:cache)
|
31
|
-
|
32
|
-
model_class = attributes.model_class
|
33
|
-
model_class.include BackendResetter.for(model_class).new(attributes.names) { clear_cache }
|
29
|
+
included_hook do |_, backend_class|
|
30
|
+
if options[:cache]
|
31
|
+
backend_class.include(BackendMethods) unless backend_class.apply_plugin(:cache)
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
35
|
+
private
|
36
|
+
|
37
|
+
# Used in ORM cache plugins
|
38
|
+
def define_cache_hooks(klass, *reset_methods)
|
39
|
+
mod = self
|
40
|
+
private_methods = reset_methods & klass.private_instance_methods
|
41
|
+
reset_methods.each do |method_name|
|
42
|
+
define_method method_name do |*args|
|
43
|
+
super(*args).tap do
|
44
|
+
mod.names.each { |name| mobility_backends[name].clear_cache }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
klass.class_eval { private(*private_methods) }
|
49
|
+
end
|
50
|
+
|
51
|
+
module BackendMethods
|
52
|
+
# @group Backend Accessors
|
53
|
+
#
|
54
|
+
# @!macro backend_reader
|
55
|
+
# @!method read(locale, value, options = {})
|
56
|
+
# @option options [Boolean] cache *false* to disable cache.
|
57
|
+
def read(locale, **options)
|
58
|
+
return super(locale, options) if options.delete(:cache) == false
|
59
|
+
if cache.has_key?(locale)
|
60
|
+
cache[locale]
|
61
|
+
else
|
62
|
+
cache[locale] = super(locale, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!macro backend_writer
|
67
|
+
# @option options [Boolean] cache
|
68
|
+
# *false* to disable cache.
|
69
|
+
def write(locale, value, **options)
|
70
|
+
return super if options.delete(:cache) == false
|
71
|
+
cache[locale] = super
|
72
|
+
end
|
73
|
+
# @!endgroup
|
74
|
+
|
75
|
+
def clear_cache
|
76
|
+
@cache = {}
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def cache
|
82
|
+
@cache ||= {}
|
83
|
+
end
|
50
84
|
end
|
51
|
-
# @!endgroup
|
52
85
|
end
|
86
|
+
|
87
|
+
register_plugin(:cache, Cache)
|
53
88
|
end
|
54
89
|
end
|
@@ -6,8 +6,7 @@ module Mobility
|
|
6
6
|
|
7
7
|
Defines value or proc to fall through to if return value from getter would
|
8
8
|
otherwise be nil. This plugin is disabled by default but will be enabled if any
|
9
|
-
value
|
10
|
-
option key.
|
9
|
+
value is passed as the +default+ option key.
|
11
10
|
|
12
11
|
If default is a +Proc+, it will be called with the context of the model, and
|
13
12
|
passed arguments:
|
@@ -63,11 +62,13 @@ The proc can accept zero to three arguments (see examples below)
|
|
63
62
|
#=> "Post"
|
64
63
|
=end
|
65
64
|
module Default
|
65
|
+
extend Plugin
|
66
|
+
|
67
|
+
requires :backend, include: :before
|
68
|
+
|
66
69
|
# Applies default plugin to attributes.
|
67
|
-
|
68
|
-
|
69
|
-
def self.apply(attributes, option)
|
70
|
-
attributes.backend_class.include(self) unless option == Plugins::OPTION_UNSET
|
70
|
+
included_hook do |_klass, backend_class|
|
71
|
+
backend_class.include(BackendMethods)
|
71
72
|
end
|
72
73
|
|
73
74
|
# Generate a default value for given parameters.
|
@@ -82,19 +83,23 @@ The proc can accept zero to three arguments (see examples below)
|
|
82
83
|
model.instance_exec(*args, &default_value)
|
83
84
|
end
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
86
|
+
module BackendMethods
|
87
|
+
# @!group Backend Accessors
|
88
|
+
# @!macro backend_reader
|
89
|
+
# @option accessor_options [Boolean] default
|
90
|
+
# *false* to disable presence filter.
|
91
|
+
def read(locale, accessor_options = {})
|
92
|
+
default = accessor_options.has_key?(:default) ? accessor_options.delete(:default) : options[:default]
|
93
|
+
if (value = super(locale, accessor_options)).nil?
|
94
|
+
Default[default, locale: locale, accessor_options: accessor_options, model: model, attribute: attribute]
|
95
|
+
else
|
96
|
+
value
|
97
|
+
end
|
95
98
|
end
|
99
|
+
# @!endgroup
|
96
100
|
end
|
97
|
-
# @!endgroup
|
98
101
|
end
|
102
|
+
|
103
|
+
register_plugin(:default, Default)
|
99
104
|
end
|
100
105
|
end
|