mobility 1.1.0 → 1.2.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
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile +2 -2
- data/Gemfile.lock +7 -22
- data/README.md +7 -8
- data/lib/mobility/backend.rb +82 -26
- data/lib/mobility/backends/active_record/key_value.rb +68 -49
- data/lib/mobility/backends/active_record/pg_hash.rb +1 -1
- data/lib/mobility/backends/sequel/container.rb +2 -4
- data/lib/mobility/backends/sequel/key_value.rb +63 -54
- data/lib/mobility/backends/sequel/pg_hash.rb +4 -6
- data/lib/mobility/backends/sequel/table.rb +3 -5
- data/lib/mobility/backends/serialized.rb +1 -1
- data/lib/mobility/plugins/active_record/column_fallback.rb +139 -0
- data/lib/mobility/plugins/active_record/query.rb +5 -0
- data/lib/mobility/plugins/active_record.rb +2 -0
- data/lib/mobility/plugins/column_fallback.rb +15 -0
- data/lib/mobility/plugins/fallthrough_accessors.rb +7 -0
- data/lib/mobility/plugins/sequel/column_fallback.rb +133 -0
- data/lib/mobility/plugins/sequel/query.rb +1 -1
- data/lib/mobility/plugins/sequel.rb +2 -0
- data/lib/mobility/version.rb +1 -1
- data/lib/mobility.rb +3 -0
- data.tar.gz.sig +0 -0
- metadata +27 -24
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5cedc8fabef42f478f93a4bb4fa1e8764759855dcded4d25f397c018f1811d2
|
|
4
|
+
data.tar.gz: 1fb647fdbcbafa3b8a5a6d47d71500e2481556aa72674c5269ea0dc560d63ca0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ee138cb5544fc8942dc8a6dbf7fcfd54bf3829329075950755b8fea3ba676c430b880b14366dcfc09b66651b06577957e02130c5b4f2a62d7db96d02f1a150c8
|
|
7
|
+
data.tar.gz: 980502d8c285bedf8e2958a24cb77c129ee39f1cfb946c44bbd83f7cc89b30009a829bfb17b515aac9e02d3f3a810b1433cab416aabec67c1daab9c6f100683d
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,37 @@
|
|
|
1
1
|
# Mobility Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
- Assign blank values in pg hash backends
|
|
5
|
+
([#516](https://github.com/shioyama/mobility/pull/516))
|
|
6
|
+
|
|
3
7
|
## 1.1
|
|
4
8
|
|
|
9
|
+
## 1.2
|
|
10
|
+
|
|
11
|
+
### 1.2.0
|
|
12
|
+
- Add ColumnFallback plugin
|
|
13
|
+
([#512](https://github.com/shioyama/mobility/pull/512))
|
|
14
|
+
- Fix Sequel querying on untranslated attributes in `i18n` block
|
|
15
|
+
([#529](https://github.com/shioyama/mobility/pull/529))
|
|
16
|
+
- Allow passing configured backend class as third argument to setup
|
|
17
|
+
([#528](https://github.com/shioyama/mobility/pull/528))
|
|
18
|
+
- Clearly distinguish backend classes from their configured subclasses
|
|
19
|
+
([#527](Clearly distinguish backend classes from their configured subclasses))
|
|
20
|
+
|
|
21
|
+
### 1.1.3
|
|
22
|
+
- Do not swallow keyword args on ruby 3 in fallthrough accessors
|
|
23
|
+
([#520](https://github.com/shioyama/mobility/pull/520)) thanks
|
|
24
|
+
[doits](https://github.com/doits)!
|
|
25
|
+
- Assign blank values in pg hash backends
|
|
26
|
+
([#516](https://github.com/shioyama/mobility/pull/516))
|
|
27
|
+
|
|
28
|
+
### 1.1.2
|
|
29
|
+
- Check whether class responds to mobility_attribute?
|
|
30
|
+
([#515](https://github.com/shioyama/mobility/pull/515))
|
|
31
|
+
|
|
32
|
+
### 1.1.1
|
|
33
|
+
- Updated signing key
|
|
34
|
+
|
|
5
35
|
### 1.1.0
|
|
6
36
|
- Remove `Mobility::Plugins::Attributes#each`
|
|
7
37
|
([#475](https://github.com/shioyama/mobility/pull/475))
|
data/Gemfile
CHANGED
|
@@ -12,8 +12,8 @@ group :development, :test do
|
|
|
12
12
|
case orm_version
|
|
13
13
|
when '4.2', '5.0', '5.1', '5.2', '6.0', '6.1'
|
|
14
14
|
gem 'activerecord', "~> #{orm_version}.0"
|
|
15
|
-
when '
|
|
16
|
-
git 'https://github.com/rails/rails.git' do
|
|
15
|
+
when '7.0'
|
|
16
|
+
git 'https://github.com/rails/rails.git', branch: 'main' do
|
|
17
17
|
gem 'activerecord'
|
|
18
18
|
gem 'activesupport'
|
|
19
19
|
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,31 +1,20 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
mobility (1.0.
|
|
4
|
+
mobility (1.2.0.alpha)
|
|
5
5
|
i18n (>= 0.6.10, < 2)
|
|
6
6
|
request_store (~> 1.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
activemodel (6.0.3.4)
|
|
12
|
-
activesupport (= 6.0.3.4)
|
|
13
|
-
activerecord (6.0.3.4)
|
|
14
|
-
activemodel (= 6.0.3.4)
|
|
15
|
-
activesupport (= 6.0.3.4)
|
|
16
|
-
activesupport (6.0.3.4)
|
|
17
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
18
|
-
i18n (>= 0.7, < 2)
|
|
19
|
-
minitest (~> 5.1)
|
|
20
|
-
tzinfo (~> 1.1)
|
|
21
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
|
22
11
|
benchmark-ips (2.8.4)
|
|
23
12
|
byebug (11.1.3)
|
|
24
13
|
coderay (1.1.3)
|
|
25
|
-
concurrent-ruby (1.1.
|
|
14
|
+
concurrent-ruby (1.1.9)
|
|
26
15
|
database_cleaner (1.99.0)
|
|
27
16
|
diff-lcs (1.4.4)
|
|
28
|
-
ffi (1.
|
|
17
|
+
ffi (1.15.0)
|
|
29
18
|
formatador (0.2.5)
|
|
30
19
|
guard (2.16.2)
|
|
31
20
|
formatador (>= 0.2.4)
|
|
@@ -41,14 +30,13 @@ GEM
|
|
|
41
30
|
guard (~> 2.1)
|
|
42
31
|
guard-compat (~> 1.1)
|
|
43
32
|
rspec (>= 2.99.0, < 4.0)
|
|
44
|
-
i18n (1.8.
|
|
33
|
+
i18n (1.8.10)
|
|
45
34
|
concurrent-ruby (~> 1.0)
|
|
46
|
-
listen (3.
|
|
35
|
+
listen (3.5.1)
|
|
47
36
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
48
37
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
49
38
|
lumberjack (1.2.8)
|
|
50
39
|
method_source (1.0.0)
|
|
51
|
-
minitest (5.14.3)
|
|
52
40
|
nenv (0.3.0)
|
|
53
41
|
notiffany (0.1.3)
|
|
54
42
|
nenv (~> 0.1)
|
|
@@ -80,19 +68,15 @@ GEM
|
|
|
80
68
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
69
|
rspec-support (~> 3.10.0)
|
|
82
70
|
rspec-support (3.10.2)
|
|
71
|
+
sequel (5.48.0)
|
|
83
72
|
shellany (0.0.1)
|
|
84
73
|
thor (1.1.0)
|
|
85
|
-
thread_safe (0.3.6)
|
|
86
|
-
tzinfo (1.2.9)
|
|
87
|
-
thread_safe (~> 0.1)
|
|
88
74
|
yard (0.9.26)
|
|
89
|
-
zeitwerk (2.4.2)
|
|
90
75
|
|
|
91
76
|
PLATFORMS
|
|
92
77
|
ruby
|
|
93
78
|
|
|
94
79
|
DEPENDENCIES
|
|
95
|
-
activerecord (~> 6.0.0)
|
|
96
80
|
benchmark-ips
|
|
97
81
|
database_cleaner (~> 1.5, >= 1.5.3)
|
|
98
82
|
guard-rspec
|
|
@@ -101,6 +85,7 @@ DEPENDENCIES
|
|
|
101
85
|
pry-byebug
|
|
102
86
|
rake (~> 12, >= 12.2.1)
|
|
103
87
|
rspec (~> 3.0)
|
|
88
|
+
sequel (~> 5.0)
|
|
104
89
|
yard (~> 0.9.0)
|
|
105
90
|
|
|
106
91
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Mobility
|
|
|
12
12
|
[docs]: http://www.rubydoc.info/gems/mobility
|
|
13
13
|
[wiki]: https://github.com/shioyama/mobility/wiki
|
|
14
14
|
|
|
15
|
-
**This is the readme for version 1.
|
|
15
|
+
**This is the readme for version 1.x of Mobility. If you are using an earlier
|
|
16
16
|
version (0.8.x or earlier), you probably want the readme on the [0-8
|
|
17
17
|
branch](https://github.com/shioyama/mobility/tree/0-8).**
|
|
18
18
|
|
|
@@ -55,7 +55,7 @@ Installation
|
|
|
55
55
|
Add this line to your application's Gemfile:
|
|
56
56
|
|
|
57
57
|
```ruby
|
|
58
|
-
gem 'mobility', '~> 1.
|
|
58
|
+
gem 'mobility', '~> 1.2.0'
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
### ActiveRecord (Rails)
|
|
@@ -130,9 +130,7 @@ default `type` option for the KeyValue backend to `:string`.
|
|
|
130
130
|
end
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
-
We will assume the configuration above in the examples that follow.
|
|
134
|
-
configuration options are described in the [API
|
|
135
|
-
docs](http://www.rubydoc.info/gems/mobility/Mobility/Configuration).
|
|
133
|
+
We will assume the configuration above in the examples that follow.
|
|
136
134
|
|
|
137
135
|
See [Getting Started](#quickstart) to get started translating your models.
|
|
138
136
|
|
|
@@ -486,9 +484,7 @@ end
|
|
|
486
484
|
```
|
|
487
485
|
|
|
488
486
|
Internally, Mobility assigns the fallbacks hash to an instance of
|
|
489
|
-
`I18n::Locale::Fallbacks.new
|
|
490
|
-
`fallbacks_generator` configuration option, see the [API documentation on
|
|
491
|
-
configuration](http://www.rubydoc.info/gems/mobility/Mobility/Configuration)).
|
|
487
|
+
`I18n::Locale::Fallbacks.new`.
|
|
492
488
|
|
|
493
489
|
By setting fallbacks for German and French to Japanese, values will fall
|
|
494
490
|
through to the Japanese value if none is present for either of these locales,
|
|
@@ -1028,6 +1024,9 @@ Integrations
|
|
|
1028
1024
|
* [mobility-ransack](https://github.com/shioyama/mobility-ransack): Search
|
|
1029
1025
|
attributes translated by Mobility with
|
|
1030
1026
|
[Ransack](https://github.com/activerecord-hackery/ransack).
|
|
1027
|
+
* [mobility-actiontext](https://github.com/sedubois/mobility-actiontext): Translate
|
|
1028
|
+
Rails [Action Text](https://guides.rubyonrails.org/action_text_overview.html) rich text
|
|
1029
|
+
with Mobility.
|
|
1031
1030
|
|
|
1032
1031
|
Tutorials
|
|
1033
1032
|
---------
|
data/lib/mobility/backend.rb
CHANGED
|
@@ -21,8 +21,8 @@ On top of this, a backend will normally:
|
|
|
21
21
|
corresponding to valid keys for configuring this backend.
|
|
22
22
|
- implement a +configure+ class method to apply any normalization to the
|
|
23
23
|
keys on the options hash included in +valid_keys+
|
|
24
|
-
- call the +setup+ method yielding attributes and options
|
|
25
|
-
model class
|
|
24
|
+
- call the +setup+ method yielding attributes and options (and optionally the
|
|
25
|
+
configured backend class) to configure the model class
|
|
26
26
|
|
|
27
27
|
@example Defining a Backend
|
|
28
28
|
class MyBackend
|
|
@@ -47,6 +47,13 @@ On top of this, a backend will normally:
|
|
|
47
47
|
setup do |attributes, options|
|
|
48
48
|
# Do something with attributes and options in context of model class.
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
# The block can optionally take the configured backend class as its third
|
|
52
|
+
# argument:
|
|
53
|
+
#
|
|
54
|
+
# setup do |attributes, options, backend_class|
|
|
55
|
+
# ...
|
|
56
|
+
# end
|
|
50
57
|
end
|
|
51
58
|
|
|
52
59
|
@see Mobility::Translations
|
|
@@ -117,7 +124,6 @@ On top of this, a backend will normally:
|
|
|
117
124
|
# Extend included class with +setup+ method and other class methods
|
|
118
125
|
def self.included(base)
|
|
119
126
|
base.extend ClassMethods
|
|
120
|
-
base.singleton_class.attr_reader :options, :model_class
|
|
121
127
|
end
|
|
122
128
|
|
|
123
129
|
# Defines setup hooks for backend to customize model class.
|
|
@@ -136,9 +142,11 @@ On top of this, a backend will normally:
|
|
|
136
142
|
def setup &block
|
|
137
143
|
if @setup_block
|
|
138
144
|
setup_block = @setup_block
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
exec_setup_block = method(:exec_setup_block)
|
|
146
|
+
@setup_block = lambda do |attributes, options, backend_class|
|
|
147
|
+
[setup_block, block].each do |blk|
|
|
148
|
+
exec_setup_block.call(self, attributes, options, backend_class, &blk)
|
|
149
|
+
end
|
|
142
150
|
end
|
|
143
151
|
else
|
|
144
152
|
@setup_block = block
|
|
@@ -147,17 +155,6 @@ On top of this, a backend will normally:
|
|
|
147
155
|
|
|
148
156
|
def inherited(subclass)
|
|
149
157
|
subclass.instance_variable_set(:@setup_block, @setup_block)
|
|
150
|
-
subclass.instance_variable_set(:@options, @options)
|
|
151
|
-
subclass.instance_variable_set(:@model_class, @model_class)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Call setup block on a class with attributes and options.
|
|
155
|
-
# @param model_class Class to be setup-ed
|
|
156
|
-
# @param [Array<String>] attribute_names
|
|
157
|
-
# @param [Hash] options
|
|
158
|
-
def setup_model(model_class, attribute_names)
|
|
159
|
-
return unless setup_block = @setup_block
|
|
160
|
-
model_class.class_exec(attribute_names, options, &setup_block)
|
|
161
158
|
end
|
|
162
159
|
|
|
163
160
|
# Build a subclass of this backend class for a given set of options
|
|
@@ -167,11 +164,7 @@ On top of this, a backend will normally:
|
|
|
167
164
|
# @param [Hash] options
|
|
168
165
|
# @return [Class] backend subclass
|
|
169
166
|
def build_subclass(model_class, options)
|
|
170
|
-
|
|
171
|
-
@model_class = model_class
|
|
172
|
-
configure(options) if respond_to?(:configure)
|
|
173
|
-
@options = options.freeze
|
|
174
|
-
end
|
|
167
|
+
ConfiguredBackend.build(self, model_class, options)
|
|
175
168
|
end
|
|
176
169
|
|
|
177
170
|
# Create instance and class methods to access value on options hash
|
|
@@ -188,10 +181,30 @@ On top of this, a backend will normally:
|
|
|
188
181
|
EOM
|
|
189
182
|
end
|
|
190
183
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
184
|
+
def options
|
|
185
|
+
raise_unconfigured!(:options)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def model_class
|
|
189
|
+
raise_unconfigured!(:model_class)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def setup_model(_model_class, _attributes)
|
|
193
|
+
raise_unconfigured!(:setup_model)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def raise_unconfigured!(method_name)
|
|
199
|
+
raise UnconfiguredError, "You are calling #{method_name} on an unconfigured backend class."
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def exec_setup_block(model_class, *args, &block)
|
|
203
|
+
if block.arity == 3
|
|
204
|
+
model_class.class_exec(*args[0..2], &block)
|
|
205
|
+
else
|
|
206
|
+
model_class.class_exec(*args[0..1], &block)
|
|
207
|
+
end
|
|
195
208
|
end
|
|
196
209
|
end
|
|
197
210
|
|
|
@@ -204,5 +217,48 @@ On top of this, a backend will normally:
|
|
|
204
217
|
backend.write(locale, value, options)
|
|
205
218
|
end
|
|
206
219
|
end
|
|
220
|
+
|
|
221
|
+
class ConfiguredError < StandardError; end
|
|
222
|
+
class UnconfiguredError < StandardError; end
|
|
223
|
+
=begin
|
|
224
|
+
|
|
225
|
+
Module included in configured backend classes, which in addition to methods on
|
|
226
|
+
the parent backend class also have a +model_class+ and set of +options+.
|
|
227
|
+
|
|
228
|
+
=end
|
|
229
|
+
module ConfiguredBackend
|
|
230
|
+
def self.build(backend_class, model_class, options)
|
|
231
|
+
Class.new(backend_class) do
|
|
232
|
+
extend ConfiguredBackend
|
|
233
|
+
|
|
234
|
+
@model_class = model_class
|
|
235
|
+
configure(options) if respond_to?(:configure)
|
|
236
|
+
@options = options.freeze
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def self.extended(klass)
|
|
241
|
+
klass.singleton_class.attr_reader :options, :model_class
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Call setup block on a class with attributes and options.
|
|
245
|
+
# @param model_class Class to be setup-ed
|
|
246
|
+
# @param [Array<String>] attribute_names
|
|
247
|
+
# @param [Hash] options
|
|
248
|
+
def setup_model(model_class, attribute_names)
|
|
249
|
+
return unless setup_block = @setup_block
|
|
250
|
+
exec_setup_block(model_class, attribute_names, options, self, &setup_block)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def inherited(_)
|
|
254
|
+
raise ConfiguredError, "Configured backends cannot be subclassed."
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Show subclassed backend class name, if it has one.
|
|
258
|
+
# @return [String]
|
|
259
|
+
def inspect
|
|
260
|
+
(name = superclass.name) ? "#<#{name}>" : super
|
|
261
|
+
end
|
|
262
|
+
end
|
|
207
263
|
end
|
|
208
264
|
end
|
|
@@ -65,6 +65,69 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
69
|
+
def define_has_many_association(klass, attributes)
|
|
70
|
+
# Track all attributes for this association, so that we can limit the scope
|
|
71
|
+
# of keys for the association to only these attributes. We need to track the
|
|
72
|
+
# attributes assigned to the association in case this setup code is called
|
|
73
|
+
# multiple times, so we don't "forget" earlier attributes.
|
|
74
|
+
#
|
|
75
|
+
attrs_method_name = :"__#{association_name}_attributes"
|
|
76
|
+
association_attributes = (klass.instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
77
|
+
klass.instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
78
|
+
|
|
79
|
+
b = self
|
|
80
|
+
|
|
81
|
+
klass.has_many association_name, ->{ where b.key_column => association_attributes },
|
|
82
|
+
as: belongs_to,
|
|
83
|
+
class_name: class_name.name,
|
|
84
|
+
inverse_of: belongs_to,
|
|
85
|
+
autosave: true
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
89
|
+
def define_initialize_dup(klass)
|
|
90
|
+
b = self
|
|
91
|
+
module_name = "MobilityArKeyValue#{association_name.to_s.camelcase}"
|
|
92
|
+
unless const_defined?(module_name)
|
|
93
|
+
callback_methods = Module.new do
|
|
94
|
+
define_method :initialize_dup do |source|
|
|
95
|
+
super(source)
|
|
96
|
+
self.send("#{b.association_name}=", source.send(b.association_name).map(&:dup))
|
|
97
|
+
# Set inverse on associations
|
|
98
|
+
send(b.association_name).each do |translation|
|
|
99
|
+
translation.send(:"#{b.belongs_to}=", self)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
klass.include const_set(module_name, callback_methods)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
108
|
+
def define_before_save_callback(klass)
|
|
109
|
+
b = self
|
|
110
|
+
klass.before_save do
|
|
111
|
+
send(b.association_name).select { |t| t.send(b.value_column).blank? }.each do |translation|
|
|
112
|
+
send(b.association_name).destroy(translation)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
118
|
+
def define_after_destroy_callback(klass)
|
|
119
|
+
# Ensure we only call after destroy hook once per translations class
|
|
120
|
+
b = self
|
|
121
|
+
translation_classes = [class_name, *Mobility::Backends::ActiveRecord::KeyValue::Translation.descendants].uniq
|
|
122
|
+
klass.after_destroy do
|
|
123
|
+
@mobility_after_destroy_translation_classes = [] unless defined?(@mobility_after_destroy_translation_classes)
|
|
124
|
+
(translation_classes - @mobility_after_destroy_translation_classes).each do |translation_class|
|
|
125
|
+
translation_class.where(b.belongs_to => self).destroy_all
|
|
126
|
+
end
|
|
127
|
+
@mobility_after_destroy_translation_classes += translation_classes
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
68
131
|
private
|
|
69
132
|
|
|
70
133
|
def join_translations(relation, key, locale, join_type)
|
|
@@ -149,55 +212,11 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
149
212
|
end
|
|
150
213
|
end
|
|
151
214
|
|
|
152
|
-
setup do |attributes,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
belongs_to = options[:belongs_to]
|
|
158
|
-
|
|
159
|
-
# Track all attributes for this association, so that we can limit the scope
|
|
160
|
-
# of keys for the association to only these attributes. We need to track the
|
|
161
|
-
# attributes assigned to the association in case this setup code is called
|
|
162
|
-
# multiple times, so we don't "forget" earlier attributes.
|
|
163
|
-
#
|
|
164
|
-
attrs_method_name = :"__#{association_name}_attributes"
|
|
165
|
-
association_attributes = (instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
166
|
-
instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
167
|
-
|
|
168
|
-
has_many association_name, ->{ where key_column => association_attributes },
|
|
169
|
-
as: belongs_to,
|
|
170
|
-
class_name: translation_class.name,
|
|
171
|
-
inverse_of: belongs_to,
|
|
172
|
-
autosave: true
|
|
173
|
-
before_save do
|
|
174
|
-
send(association_name).select { |t| t.send(value_column).blank? }.each do |translation|
|
|
175
|
-
send(association_name).destroy(translation)
|
|
176
|
-
end
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
module_name = "MobilityArKeyValue#{association_name.to_s.camelcase}"
|
|
180
|
-
unless const_defined?(module_name)
|
|
181
|
-
callback_methods = Module.new do
|
|
182
|
-
define_method :initialize_dup do |source|
|
|
183
|
-
super(source)
|
|
184
|
-
self.send("#{association_name}=", source.send(association_name).map(&:dup))
|
|
185
|
-
# Set inverse on associations
|
|
186
|
-
send(association_name).each do |translation|
|
|
187
|
-
translation.send(:"#{belongs_to}=", self)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
include const_set(module_name, callback_methods)
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
# Ensure we only call after destroy hook once per translations class
|
|
195
|
-
translation_classes = [translation_class, *Mobility::Backends::ActiveRecord::KeyValue::Translation.descendants].uniq
|
|
196
|
-
after_destroy do
|
|
197
|
-
@mobility_after_destroy_translation_classes = [] unless defined?(@mobility_after_destroy_translation_classes)
|
|
198
|
-
(translation_classes - @mobility_after_destroy_translation_classes).each { |klass| klass.where(belongs_to => self).destroy_all }
|
|
199
|
-
@mobility_after_destroy_translation_classes += translation_classes
|
|
200
|
-
end
|
|
215
|
+
setup do |attributes, _options, backend_class|
|
|
216
|
+
backend_class.define_has_many_association(self, attributes)
|
|
217
|
+
backend_class.define_initialize_dup(self)
|
|
218
|
+
backend_class.define_before_save_callback(self)
|
|
219
|
+
backend_class.define_after_destroy_callback(self)
|
|
201
220
|
end
|
|
202
221
|
|
|
203
222
|
# Returns translation for a given locale, or builds one if none is present.
|
|
@@ -32,7 +32,7 @@ Internal class used by ActiveRecord backends backed by a Postgres data type
|
|
|
32
32
|
def self.dump(obj)
|
|
33
33
|
if obj.is_a? ::Hash
|
|
34
34
|
obj.inject({}) do |translations, (locale, value)|
|
|
35
|
-
translations[locale] = value
|
|
35
|
+
translations[locale] = value unless value.nil?
|
|
36
36
|
translations
|
|
37
37
|
end
|
|
38
38
|
else
|
|
@@ -57,9 +57,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
|
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
setup do |attributes, options|
|
|
60
|
+
setup do |attributes, options, backend_class|
|
|
63
61
|
column_name = options[:column_name]
|
|
64
62
|
mod = Module.new do
|
|
65
63
|
define_method :before_validation do
|
|
@@ -71,7 +69,7 @@ Implements the {Mobility::Backends::Container} backend for Sequel models.
|
|
|
71
69
|
end
|
|
72
70
|
end
|
|
73
71
|
include mod
|
|
74
|
-
|
|
72
|
+
backend_class.define_hash_initializer(mod, [column_name])
|
|
75
73
|
|
|
76
74
|
plugin :defaults_setter
|
|
77
75
|
attributes.each { |attribute| default_values[attribute.to_sym] = {} }
|
|
@@ -51,6 +51,63 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
55
|
+
def define_one_to_many_association(klass, attributes)
|
|
56
|
+
belongs_to_id = :"#{belongs_to}_id"
|
|
57
|
+
belongs_to_type = :"#{belongs_to}_type"
|
|
58
|
+
|
|
59
|
+
# Track all attributes for this association, so that we can limit the scope
|
|
60
|
+
# of keys for the association to only these attributes. We need to track the
|
|
61
|
+
# attributes assigned to the association in case this setup code is called
|
|
62
|
+
# multiple times, so we don't "forget" earlier attributes.
|
|
63
|
+
#
|
|
64
|
+
attrs_method_name = :"#{association_name}_attributes"
|
|
65
|
+
association_attributes = (klass.instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
66
|
+
klass.instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
67
|
+
|
|
68
|
+
klass.one_to_many association_name,
|
|
69
|
+
reciprocal: belongs_to,
|
|
70
|
+
key: belongs_to_id,
|
|
71
|
+
reciprocal_type: :one_to_many,
|
|
72
|
+
conditions: { belongs_to_type => klass.to_s, key_column => association_attributes },
|
|
73
|
+
adder: proc { |translation| translation.update(belongs_to_id => pk, belongs_to_type => self.class.to_s) },
|
|
74
|
+
remover: proc { |translation| translation.update(belongs_to_id => nil, belongs_to_type => nil) },
|
|
75
|
+
clearer: proc { send_(:"#{association_name}_dataset").update(belongs_to_id => nil, belongs_to_type => nil) },
|
|
76
|
+
class: class_name
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
80
|
+
def define_save_callbacks(klass, attributes)
|
|
81
|
+
b = self
|
|
82
|
+
callback_methods = Module.new do
|
|
83
|
+
define_method :before_save do
|
|
84
|
+
super()
|
|
85
|
+
send(b.association_name).select { |t| attributes.include?(t.__send__(b.key_column)) && Util.blank?(t.__send__(b.value_column)) }.each(&:destroy)
|
|
86
|
+
end
|
|
87
|
+
define_method :after_save do
|
|
88
|
+
super()
|
|
89
|
+
attributes.each { |attribute| mobility_backends[attribute].save_translations }
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
klass.include callback_methods
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Called from setup block. Can be overridden to customize behaviour.
|
|
96
|
+
def define_after_destroy_callback(klass)
|
|
97
|
+
# Clean up *all* leftover translations of this model, only once.
|
|
98
|
+
b = self
|
|
99
|
+
translation_classes = [class_name, *Mobility::Backends::Sequel::KeyValue::Translation.descendants].uniq
|
|
100
|
+
klass.define_method :after_destroy do
|
|
101
|
+
super()
|
|
102
|
+
|
|
103
|
+
@mobility_after_destroy_translation_classes = [] unless defined?(@mobility_after_destroy_translation_classes)
|
|
104
|
+
(translation_classes - @mobility_after_destroy_translation_classes).each do |translation_class|
|
|
105
|
+
translation_class.where(:"#{b.belongs_to}_id" => id, :"#{b.belongs_to}_type" => self.class.name).destroy
|
|
106
|
+
end
|
|
107
|
+
@mobility_after_destroy_translation_classes += translation_classes
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
54
111
|
private
|
|
55
112
|
|
|
56
113
|
def join_translations(dataset, attr, locale, join_type)
|
|
@@ -74,7 +131,7 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
74
131
|
visit_sql_identifier(predicate, locale)
|
|
75
132
|
when ::Sequel::SQL::BooleanExpression
|
|
76
133
|
visit_boolean(predicate, locale)
|
|
77
|
-
when ::Sequel::SQL::
|
|
134
|
+
when ::Sequel::SQL::ComplexExpression
|
|
78
135
|
visit(predicate.args, locale)
|
|
79
136
|
else
|
|
80
137
|
{}
|
|
@@ -123,61 +180,13 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
123
180
|
end
|
|
124
181
|
end
|
|
125
182
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
translation_class = options[:class_name]
|
|
131
|
-
key_column = options[:key_column]
|
|
132
|
-
value_column = options[:value_column]
|
|
133
|
-
belongs_to = options[:belongs_to]
|
|
134
|
-
belongs_to_id = :"#{belongs_to}_id"
|
|
135
|
-
belongs_to_type = :"#{belongs_to}_type"
|
|
136
|
-
|
|
137
|
-
# Track all attributes for this association, so that we can limit the scope
|
|
138
|
-
# of keys for the association to only these attributes. We need to track the
|
|
139
|
-
# attributes assigned to the association in case this setup code is called
|
|
140
|
-
# multiple times, so we don't "forget" earlier attributes.
|
|
141
|
-
#
|
|
142
|
-
attrs_method_name = :"#{association_name}_attributes"
|
|
143
|
-
association_attributes = (instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
144
|
-
instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
145
|
-
|
|
146
|
-
one_to_many association_name,
|
|
147
|
-
reciprocal: belongs_to,
|
|
148
|
-
key: belongs_to_id,
|
|
149
|
-
reciprocal_type: :one_to_many,
|
|
150
|
-
conditions: { belongs_to_type => self.to_s, key_column => association_attributes },
|
|
151
|
-
adder: proc { |translation| translation.update(belongs_to_id => pk, belongs_to_type => self.class.to_s) },
|
|
152
|
-
remover: proc { |translation| translation.update(belongs_to_id => nil, belongs_to_type => nil) },
|
|
153
|
-
clearer: proc { send_(:"#{association_name}_dataset").update(belongs_to_id => nil, belongs_to_type => nil) },
|
|
154
|
-
class: translation_class
|
|
155
|
-
|
|
156
|
-
callback_methods = Module.new do
|
|
157
|
-
define_method :before_save do
|
|
158
|
-
super()
|
|
159
|
-
send(association_name).select { |t| attributes.include?(t.__send__(key_column)) && Util.blank?(t.__send__(value_column)) }.each(&:destroy)
|
|
160
|
-
end
|
|
161
|
-
define_method :after_save do
|
|
162
|
-
super()
|
|
163
|
-
attributes.each { |attribute| mobility_backends[attribute].save_translations }
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
include callback_methods
|
|
167
|
-
|
|
168
|
-
# Clean up *all* leftover translations of this model, only once.
|
|
169
|
-
translation_classes = [translation_class, *Mobility::Backends::Sequel::KeyValue::Translation.descendants].uniq
|
|
170
|
-
define_method :after_destroy do
|
|
171
|
-
super()
|
|
183
|
+
setup do |attributes, _options, backend_class|
|
|
184
|
+
backend_class.define_one_to_many_association(self, attributes)
|
|
185
|
+
backend_class.define_save_callbacks(self, attributes)
|
|
186
|
+
backend_class.define_after_destroy_callback(self)
|
|
172
187
|
|
|
173
|
-
@mobility_after_destroy_translation_classes = [] unless defined?(@mobility_after_destroy_translation_classes)
|
|
174
|
-
(translation_classes - @mobility_after_destroy_translation_classes).each do |klass|
|
|
175
|
-
klass.where(belongs_to_id => id, belongs_to_type => self.class.name).destroy
|
|
176
|
-
end
|
|
177
|
-
@mobility_after_destroy_translation_classes += translation_classes
|
|
178
|
-
end
|
|
179
188
|
include(mod = Module.new)
|
|
180
|
-
|
|
189
|
+
backend_class.define_column_changes(mod, attributes)
|
|
181
190
|
end
|
|
182
191
|
|
|
183
192
|
# Returns translation for a given locale, or initializes one if none is present.
|
|
@@ -33,22 +33,20 @@ jsonb).
|
|
|
33
33
|
model[column_name.to_sym]
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
setup do |attributes, options|
|
|
36
|
+
setup do |attributes, options, backend_class|
|
|
39
37
|
columns = attributes.map { |attribute| (options[:column_affix] % attribute).to_sym }
|
|
40
38
|
|
|
41
39
|
mod = Module.new do
|
|
42
40
|
define_method :before_validation do
|
|
43
41
|
columns.each do |column|
|
|
44
|
-
self[column].delete_if { |_, v|
|
|
42
|
+
self[column].delete_if { |_, v| v.nil? }
|
|
45
43
|
end
|
|
46
44
|
super()
|
|
47
45
|
end
|
|
48
46
|
end
|
|
49
47
|
include mod
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
backend_class.define_hash_initializer(mod, columns)
|
|
49
|
+
backend_class.define_column_changes(mod, attributes, column_affix: options[:column_affix])
|
|
52
50
|
|
|
53
51
|
plugin :defaults_setter
|
|
54
52
|
columns.each { |column| default_values[column] = {} }
|
|
@@ -84,7 +84,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
|
84
84
|
visit_sql_identifier(predicate, locale)
|
|
85
85
|
when ::Sequel::SQL::BooleanExpression
|
|
86
86
|
visit_boolean(predicate, locale)
|
|
87
|
-
when ::Sequel::SQL::
|
|
87
|
+
when ::Sequel::SQL::ComplexExpression
|
|
88
88
|
visit(predicate.args, locale)
|
|
89
89
|
else
|
|
90
90
|
nil
|
|
@@ -116,9 +116,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
setup do |attributes, options|
|
|
119
|
+
setup do |attributes, options, backend_class|
|
|
122
120
|
association_name = options[:association_name]
|
|
123
121
|
subclass_name = options[:subclass_name]
|
|
124
122
|
|
|
@@ -155,7 +153,7 @@ Implements the {Mobility::Backends::Table} backend for Sequel models.
|
|
|
155
153
|
include callback_methods
|
|
156
154
|
|
|
157
155
|
include(mod = Module.new)
|
|
158
|
-
|
|
156
|
+
backend_class.define_column_changes(mod, attributes)
|
|
159
157
|
end
|
|
160
158
|
|
|
161
159
|
def translation_for(locale, **)
|
|
@@ -40,7 +40,7 @@ Format for serialization. Either +:yaml+ (default) or +:json+.
|
|
|
40
40
|
return if obj.nil?
|
|
41
41
|
if obj.is_a? ::Hash
|
|
42
42
|
obj = obj.inject({}) do |translations, (locale, value)|
|
|
43
|
-
translations[locale] = value.to_s
|
|
43
|
+
translations[locale] = value.to_s unless value.nil?
|
|
44
44
|
translations
|
|
45
45
|
end
|
|
46
46
|
else
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Mobility
|
|
4
|
+
module Plugins
|
|
5
|
+
=begin
|
|
6
|
+
|
|
7
|
+
Plugin to use an original column for a given locale, and otherwise use the backend.
|
|
8
|
+
|
|
9
|
+
=end
|
|
10
|
+
module ActiveRecord
|
|
11
|
+
module ColumnFallback
|
|
12
|
+
extend Plugin
|
|
13
|
+
|
|
14
|
+
requires :column_fallback, include: false
|
|
15
|
+
|
|
16
|
+
included_hook do |_, backend_class|
|
|
17
|
+
case (column_fallback = options[:column_fallback])
|
|
18
|
+
when TrueClass
|
|
19
|
+
backend_class.include I18nDefaultLocaleBackend
|
|
20
|
+
when Array, Proc
|
|
21
|
+
backend_class.include BackendModule.new(column_fallback)
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError, "column_fallback value must be a boolean, an array of locales or a proc"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module I18nDefaultLocaleBackend
|
|
28
|
+
def read(locale, **)
|
|
29
|
+
locale == I18n.default_locale ? model.read_attribute(attribute) : super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def write(locale, value, **)
|
|
33
|
+
locale == I18n.default_locale ? model.send(:write_attribute, attribute, value) : super
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.included(base)
|
|
37
|
+
base.extend(ClassMethods)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
module ClassMethods
|
|
41
|
+
def build_node(attr, locale)
|
|
42
|
+
if locale == I18n.default_locale
|
|
43
|
+
model_class.arel_table[attr]
|
|
44
|
+
else
|
|
45
|
+
super
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class BackendModule < Module
|
|
52
|
+
def initialize(column_fallback)
|
|
53
|
+
case (@column_fallback = column_fallback)
|
|
54
|
+
when Array
|
|
55
|
+
define_array_accessors
|
|
56
|
+
when Proc
|
|
57
|
+
define_proc_accessors
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def included(base)
|
|
62
|
+
base.extend(ClassMethods.new(@column_fallback))
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def define_array_accessors
|
|
68
|
+
column_fallback = @column_fallback
|
|
69
|
+
|
|
70
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
|
71
|
+
def read(locale, **)
|
|
72
|
+
if #{column_fallback}.include?(locale)
|
|
73
|
+
model.read_attribute(attribute)
|
|
74
|
+
else
|
|
75
|
+
super
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def write(locale, value, **)
|
|
80
|
+
if #{column_fallback}.include?(locale)
|
|
81
|
+
model.send(:write_attribute, attribute, value)
|
|
82
|
+
else
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
EOM
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def define_proc_accessors
|
|
90
|
+
column_fallback = @column_fallback
|
|
91
|
+
|
|
92
|
+
define_method :read do |locale, **options|
|
|
93
|
+
if column_fallback.call(locale)
|
|
94
|
+
model.read_attribute(attribute)
|
|
95
|
+
else
|
|
96
|
+
super(locale, **options)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
define_method :write do |locale, value, **options|
|
|
101
|
+
if column_fallback.call(locale)
|
|
102
|
+
model.send(:write_attribute, attribute, value)
|
|
103
|
+
else
|
|
104
|
+
super(locale, value, **options)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class ClassMethods < Module
|
|
110
|
+
def initialize(column_fallback)
|
|
111
|
+
case column_fallback
|
|
112
|
+
when Array
|
|
113
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
|
114
|
+
def build_node(attr, locale)
|
|
115
|
+
if #{column_fallback}.include?(locale)
|
|
116
|
+
model_class.arel_table[attr]
|
|
117
|
+
else
|
|
118
|
+
super
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
EOM
|
|
122
|
+
when Proc
|
|
123
|
+
define_method(:build_node) do |attr, locale|
|
|
124
|
+
if column_fallback.call(locale)
|
|
125
|
+
model_class.arel_table[attr]
|
|
126
|
+
else
|
|
127
|
+
super(attr, locale)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
register_plugin(:active_record_column_fallback, ActiveRecord::ColumnFallback)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -137,6 +137,8 @@ enabled for any one attribute on the model.
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def order(opts, *rest)
|
|
140
|
+
return super unless @klass.respond_to?(:mobility_attribute?)
|
|
141
|
+
|
|
140
142
|
case opts
|
|
141
143
|
when Symbol, String
|
|
142
144
|
@klass.mobility_attribute?(opts) ? order({ opts => :asc }, *rest) : super
|
|
@@ -161,6 +163,9 @@ enabled for any one attribute on the model.
|
|
|
161
163
|
define_method method_name do |*attrs, &block|
|
|
162
164
|
return super(*attrs, &block) if (method_name == 'select' && block.present?)
|
|
163
165
|
|
|
166
|
+
if ::ActiveRecord::VERSION::STRING < '7.0'
|
|
167
|
+
return super(*attrs, &block) unless @klass.respond_to?(:mobility_attribute?)
|
|
168
|
+
end
|
|
164
169
|
return super(*attrs, &block) unless attrs.any?(&@klass.method(:mobility_attribute?))
|
|
165
170
|
|
|
166
171
|
keys = attrs.dup
|
|
@@ -4,6 +4,7 @@ require_relative "./active_record/dirty"
|
|
|
4
4
|
require_relative "./active_record/cache"
|
|
5
5
|
require_relative "./active_record/query"
|
|
6
6
|
require_relative "./active_record/uniqueness_validation"
|
|
7
|
+
require_relative "./active_record/column_fallback"
|
|
7
8
|
|
|
8
9
|
module Mobility
|
|
9
10
|
=begin
|
|
@@ -24,6 +25,7 @@ dirty for active_record_dirty) is also enabled.
|
|
|
24
25
|
requires :active_record_cache
|
|
25
26
|
requires :active_record_query
|
|
26
27
|
requires :active_record_uniqueness_validation
|
|
28
|
+
requires :active_record_column_fallback
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
included_hook do |klass|
|
|
@@ -55,6 +55,13 @@ model class is generated.
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
# Following is needed in order to not swallow `kwargs` on ruby >= 3.0.
|
|
59
|
+
# Otherwise `kwargs` are not passed by `super` to a possible other
|
|
60
|
+
# `method_missing` defined like this:
|
|
61
|
+
#
|
|
62
|
+
# def method_missing(name, *args, **kwargs, &block); end
|
|
63
|
+
ruby2_keywords :method_missing
|
|
64
|
+
|
|
58
65
|
define_method :respond_to_missing? do |method_name, include_private = false|
|
|
59
66
|
(method_name =~ method_name_regex) || super(method_name, include_private)
|
|
60
67
|
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Mobility
|
|
4
|
+
=begin
|
|
5
|
+
|
|
6
|
+
Plugin to use an original column for a given locale, and otherwise use the backend.
|
|
7
|
+
|
|
8
|
+
=end
|
|
9
|
+
module Plugins
|
|
10
|
+
module Sequel
|
|
11
|
+
module ColumnFallback
|
|
12
|
+
extend Plugin
|
|
13
|
+
|
|
14
|
+
requires :column_fallback, include: false
|
|
15
|
+
|
|
16
|
+
included_hook do |_, backend_class|
|
|
17
|
+
case (column_fallback = options[:column_fallback])
|
|
18
|
+
when TrueClass
|
|
19
|
+
backend_class.include I18nDefaultLocaleBackend
|
|
20
|
+
when Array, Proc
|
|
21
|
+
backend_class.include BackendModule.new(column_fallback)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
module I18nDefaultLocaleBackend
|
|
26
|
+
def read(locale, **)
|
|
27
|
+
locale == I18n.default_locale ? model[attribute.to_sym] : super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def write(locale, value, **)
|
|
31
|
+
if locale == I18n.default_locale
|
|
32
|
+
model[attribute.to_sym] = value
|
|
33
|
+
else
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.included(base)
|
|
39
|
+
base.extend(ClassMethods)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
module ClassMethods
|
|
43
|
+
def build_op(attr, locale)
|
|
44
|
+
if locale == I18n.default_locale
|
|
45
|
+
::Sequel::SQL::QualifiedIdentifier.new(model_class.table_name, attr.to_sym)
|
|
46
|
+
else
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class BackendModule < Module
|
|
54
|
+
def initialize(column_fallback)
|
|
55
|
+
case (@column_fallback = column_fallback)
|
|
56
|
+
when Array
|
|
57
|
+
define_array_accessors
|
|
58
|
+
when Proc
|
|
59
|
+
define_proc_accessors
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def included(base)
|
|
64
|
+
base.extend(ClassMethods.new(@column_fallback))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def define_array_accessors
|
|
70
|
+
column_fallback = @column_fallback
|
|
71
|
+
|
|
72
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
|
73
|
+
def read(locale, **)
|
|
74
|
+
#{column_fallback}.include?(locale) ? model[attribute.to_sym] : super
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def write(locale, value, **)
|
|
78
|
+
if #{column_fallback}.include?(locale)
|
|
79
|
+
model[attribute.to_sym] = value
|
|
80
|
+
else
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
EOM
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def define_proc_accessors
|
|
88
|
+
column_fallback = @column_fallback
|
|
89
|
+
|
|
90
|
+
define_method :read do |locale, **options|
|
|
91
|
+
column_fallback.call(locale) ? model[attribute.to_sym] : super(locale, **options)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
define_method :write do |locale, value, **options|
|
|
95
|
+
if column_fallback.call(locale)
|
|
96
|
+
model[attribute.to_sym] = value
|
|
97
|
+
else
|
|
98
|
+
super(locale, value, **options)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class ClassMethods < Module
|
|
104
|
+
def initialize(column_fallback)
|
|
105
|
+
case column_fallback
|
|
106
|
+
when Array
|
|
107
|
+
module_eval <<-EOM, __FILE__, __LINE__ + 1
|
|
108
|
+
def build_op(attr, locale)
|
|
109
|
+
if #{column_fallback}.include?(locale)
|
|
110
|
+
::Sequel::SQL::QualifiedIdentifier.new(model_class.table_name, attr.to_sym)
|
|
111
|
+
else
|
|
112
|
+
super
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
EOM
|
|
116
|
+
when Proc
|
|
117
|
+
define_method(:build_op) do |attr, locale|
|
|
118
|
+
if column_fallback.call(locale)
|
|
119
|
+
::Sequel::SQL::QualifiedIdentifier.new(model_class.table_name, attr.to_sym)
|
|
120
|
+
else
|
|
121
|
+
super(attr, locale)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
register_plugin(:sequel_column_fallback, Sequel::ColumnFallback)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -61,7 +61,7 @@ ActiveRecord query plugin.
|
|
|
61
61
|
locale = args[0] || @global_locale
|
|
62
62
|
@locales |= [locale]
|
|
63
63
|
@model_class.mobility_backend_class(m).build_op(m.to_s, locale)
|
|
64
|
-
elsif @model_class.columns.include?(m
|
|
64
|
+
elsif @model_class.columns.include?(m)
|
|
65
65
|
::Sequel::SQL::QualifiedIdentifier.new(@model_class.table_name, m)
|
|
66
66
|
else
|
|
67
67
|
super
|
|
@@ -9,6 +9,7 @@ require_relative "./sequel/backend"
|
|
|
9
9
|
require_relative "./sequel/dirty"
|
|
10
10
|
require_relative "./sequel/cache"
|
|
11
11
|
require_relative "./sequel/query"
|
|
12
|
+
require_relative "./sequel/column_fallback"
|
|
12
13
|
|
|
13
14
|
module Mobility
|
|
14
15
|
module Plugins
|
|
@@ -26,6 +27,7 @@ for sequel_dirty) is also enabled.
|
|
|
26
27
|
requires :sequel_dirty
|
|
27
28
|
requires :sequel_cache
|
|
28
29
|
requires :sequel_query
|
|
30
|
+
requires :sequel_column_fallback
|
|
29
31
|
|
|
30
32
|
included_hook do |klass|
|
|
31
33
|
unless sequel_class?(klass)
|
data/lib/mobility/version.rb
CHANGED
data/lib/mobility.rb
CHANGED
|
@@ -73,6 +73,9 @@ fallbacks plugin, whereas +Post+ uses +Translations+ which does not have that
|
|
|
73
73
|
plugin enabled.
|
|
74
74
|
|
|
75
75
|
=end
|
|
76
|
+
|
|
77
|
+
def ruby2_keywords(*); end unless respond_to?(:ruby2_keywords, true)
|
|
78
|
+
|
|
76
79
|
module Mobility
|
|
77
80
|
# A generic exception used by Mobility.
|
|
78
81
|
class Error < StandardError
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mobility
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Salzberg
|
|
@@ -11,30 +11,30 @@ cert_chain:
|
|
|
11
11
|
- |
|
|
12
12
|
-----BEGIN CERTIFICATE-----
|
|
13
13
|
MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhjaHJp
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
14
|
+
cy9EQz1kZWppbWF0YS9EQz1jb20wHhcNMjEwMjE4MTMxMzA5WhcNMjIwMjE4MTMx
|
|
15
|
+
MzA5WjAjMSEwHwYDVQQDDBhjaHJpcy9EQz1kZWppbWF0YS9EQz1jb20wggGiMA0G
|
|
16
|
+
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDURCKbt5oY0sCp4kYK1u5SLzVHg6Q1
|
|
17
|
+
2LejeQvUGpR3gulWqrq/507XRxE/9FSpLfgo3cGGYio1/gg2Yp7pBI4ZNEz8d2Vg
|
|
18
|
+
6caWLHYtHYF0/jlo177UspEF1bt3lCCmaA/ZyQpvoLi76Jf6VCBjepMqhLjeBSsA
|
|
19
|
+
xUqSdgNT8lzduOzdYk/GWf2Trvyz72IN6rY7hSwJ/U4R2DusRNTbKC55iyu0MyqI
|
|
20
|
+
Nks33les0xQERucqes1YzSEnpott/GUQ/fFWV1Qx7M1hMnqbQIm493BueR6X95a2
|
|
21
|
+
B7/aqY7LUVVmn9p65NMBJhbbP/pbAcLYV0C+y1Jy9NaVQTpWmJXVKBpYwlAzOJOQ
|
|
22
|
+
+b/7MBzT5Zzudkq9OlA5rZJB0hFo/Bm38MOCTSTk1/RT+zmoOyb4bx/h400L4ZUt
|
|
23
|
+
bRGON33BZ99gPiYdGfd3Pc/7FooteJASjKIO4Hman2ELRIdu6Bq+fIkTdJBcruS/
|
|
24
|
+
XL6xoRitCG7CX0IqmMKuLiKA/J0amAikHGsCAwEAAaN3MHUwCQYDVR0TBAIwADAL
|
|
25
|
+
BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFMNUGAhS68egZT6DOfJwrfIdCtT/MB0GA1Ud
|
|
26
|
+
EQQWMBSBEmNocmlzQGRlamltYXRhLmNvbTAdBgNVHRIEFjAUgRJjaHJpc0BkZWpp
|
|
27
|
+
bWF0YS5jb20wDQYJKoZIhvcNAQELBQADggGBAH1RnWhD9bum/ijqzAAlkGWYzGza
|
|
28
|
+
h/3seA2bg1r5bbttFjD48f7RepfoAMxAqfiUWcukoukJeu7UY8jWmUIn9ut1oXct
|
|
29
|
+
Fh0YnueLFzzmppCCU+/SX5mc1y7mHYZHiU5n8qy1wJ6ljLWXVeprgJ96NdnmxuVU
|
|
30
|
+
dzPPSDTex/x7xBvHiaPc/uZSLc173N3qdY/Cd0B3/OflYeU2h5UpIHnmXrsONdMC
|
|
31
|
+
Xohy+Rrr2yT09MPYG+llpLHDnXmTnPsOZUSL5Q4c/iolodv4xJZKwLMZwrm2hQl9
|
|
32
|
+
9Or9Os+qxY0zWxmWuAtTFrskLAMhckCPDEcqSZmW4CT1a/quC2Oh0y1GsXPcqtqt
|
|
33
|
+
hLRuwfTXGor6bg4CrU7GRbSqjvnBepct5lwZiZrOCnMEUpY+9Q8fwmG3o3B+wBsw
|
|
34
|
+
eBMcZq0d1tbtv1M1UXND9mOfhLZ31YvoSTPkrJiRpljUNgD0+ugelnr1/5X/9k8y
|
|
35
|
+
J9QOd3C5jpSShf/HMvpJnFuSYFm19cH9GrHjvw==
|
|
36
36
|
-----END CERTIFICATE-----
|
|
37
|
-
date: 2021-
|
|
37
|
+
date: 2021-09-26 00:00:00.000000000 Z
|
|
38
38
|
dependencies:
|
|
39
39
|
- !ruby/object:Gem::Dependency
|
|
40
40
|
name: request_store
|
|
@@ -198,6 +198,7 @@ files:
|
|
|
198
198
|
- lib/mobility/plugins/active_record.rb
|
|
199
199
|
- lib/mobility/plugins/active_record/backend.rb
|
|
200
200
|
- lib/mobility/plugins/active_record/cache.rb
|
|
201
|
+
- lib/mobility/plugins/active_record/column_fallback.rb
|
|
201
202
|
- lib/mobility/plugins/active_record/dirty.rb
|
|
202
203
|
- lib/mobility/plugins/active_record/query.rb
|
|
203
204
|
- lib/mobility/plugins/active_record/uniqueness_validation.rb
|
|
@@ -209,6 +210,7 @@ files:
|
|
|
209
210
|
- lib/mobility/plugins/backend.rb
|
|
210
211
|
- lib/mobility/plugins/backend_reader.rb
|
|
211
212
|
- lib/mobility/plugins/cache.rb
|
|
213
|
+
- lib/mobility/plugins/column_fallback.rb
|
|
212
214
|
- lib/mobility/plugins/default.rb
|
|
213
215
|
- lib/mobility/plugins/dirty.rb
|
|
214
216
|
- lib/mobility/plugins/fallbacks.rb
|
|
@@ -220,6 +222,7 @@ files:
|
|
|
220
222
|
- lib/mobility/plugins/sequel.rb
|
|
221
223
|
- lib/mobility/plugins/sequel/backend.rb
|
|
222
224
|
- lib/mobility/plugins/sequel/cache.rb
|
|
225
|
+
- lib/mobility/plugins/sequel/column_fallback.rb
|
|
223
226
|
- lib/mobility/plugins/sequel/dirty.rb
|
|
224
227
|
- lib/mobility/plugins/sequel/query.rb
|
|
225
228
|
- lib/mobility/plugins/writer.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|