mobility 0.8.8 → 1.0.0.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|