mobility 1.0.3 → 1.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGELOG.md +22 -1
- data/Gemfile.lock +13 -13
- data/README.md +3 -4
- data/lib/mobility.rb +14 -7
- data/lib/mobility/backend.rb +3 -1
- data/lib/mobility/backends/active_record/key_value.rb +28 -27
- data/lib/mobility/backends/active_record/table.rb +6 -0
- data/lib/mobility/backends/key_value.rb +11 -6
- data/lib/mobility/backends/sequel/key_value.rb +81 -47
- data/lib/mobility/plugin.rb +1 -0
- data/lib/mobility/plugins/active_record.rb +3 -1
- data/lib/mobility/plugins/active_record/backend.rb +5 -0
- data/lib/mobility/plugins/active_record/query.rb +35 -20
- data/lib/mobility/plugins/active_record/uniqueness_validation.rb +2 -2
- data/lib/mobility/plugins/attributes.rb +0 -6
- data/lib/mobility/plugins/backend.rb +10 -9
- data/lib/mobility/plugins/sequel.rb +7 -1
- data/lib/mobility/plugins/sequel/backend.rb +5 -0
- data/lib/mobility/plugins/sequel/query.rb +35 -16
- data/lib/mobility/version.rb +2 -2
- metadata +2 -2
- 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: b22675841d6b6330a4db417e3576767b42a81614b80cf376a208088ad9432de4
|
|
4
|
+
data.tar.gz: 1b2c44b38c87fc0b1e8ca5e463e9c265c09f0d307d0d663ba3aa39b6b5ca771b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 708d6ba16ec279e6a114abc811d107aa759590c711ca389d7f4b86407e100fbaed30a38b7b787ef98008291c480715949b0d4562efc2d5120eba73587fc375db
|
|
7
|
+
data.tar.gz: bd8f79dd23f7c482292c011bb800d7db20dc3b1fb06797b53fdf91ecdb9c9895c1634e3b1da06e90cff4e7b32e4aa5ebdf8f5d8b5c76f1fafbc08ca891ac99dc
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data.tar.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Mobility Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 1.1
|
|
4
|
+
|
|
5
|
+
### 1.1.0
|
|
4
6
|
- Remove `Mobility::Plugins::Attributes#each`
|
|
5
7
|
([#475](https://github.com/shioyama/mobility/pull/475))
|
|
6
8
|
- Add public method `Mobility::Plugins::ActiveRecord::Query.build_query`
|
|
@@ -21,6 +23,25 @@
|
|
|
21
23
|
v1.0](https://github.com/shioyama/mobility/wiki/Introduction-to-Mobility-v1.0)
|
|
22
24
|
for more details on how to upgrade.
|
|
23
25
|
|
|
26
|
+
### 1.0.7
|
|
27
|
+
- Require set before using Set
|
|
28
|
+
([#503](https://github.com/shioyama/mobility/pull/503))
|
|
29
|
+
|
|
30
|
+
### 1.0.6
|
|
31
|
+
- Merge options including defaults into backend options
|
|
32
|
+
([#502](https://github.com/shioyama/mobility/pull/502))
|
|
33
|
+
|
|
34
|
+
### 1.0.5
|
|
35
|
+
- Fix duping with AR Table backend, fixes
|
|
36
|
+
[#490](https://github.com/shioyama/mobility/issues/490)
|
|
37
|
+
([#499](https://github.com/shioyama/mobility/pull/499) and
|
|
38
|
+
[#501](https://github.com/shioyama/mobility/pull/501))
|
|
39
|
+
|
|
40
|
+
### 1.0.4
|
|
41
|
+
- Correctly override default backend options, fixes
|
|
42
|
+
[#492](https://github.com/shioyama/mobility/issues/492)
|
|
43
|
+
([#495](https://github.com/shioyama/mobility/pull/495))
|
|
44
|
+
|
|
24
45
|
### 1.0.3
|
|
25
46
|
- Fix `Mobility.default_backend`
|
|
26
47
|
([#497](https://github.com/shioyama/mobility/pull/497))
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
mobility (1.0.
|
|
4
|
+
mobility (1.0.7)
|
|
5
5
|
i18n (>= 0.6.10, < 2)
|
|
6
6
|
request_store (~> 1.0)
|
|
7
7
|
|
|
@@ -19,13 +19,13 @@ GEM
|
|
|
19
19
|
minitest (~> 5.1)
|
|
20
20
|
tzinfo (~> 1.1)
|
|
21
21
|
zeitwerk (~> 2.2, >= 2.2.2)
|
|
22
|
-
benchmark-ips (2.8.
|
|
22
|
+
benchmark-ips (2.8.4)
|
|
23
23
|
byebug (11.1.3)
|
|
24
24
|
coderay (1.1.3)
|
|
25
|
-
concurrent-ruby (1.1.
|
|
26
|
-
database_cleaner (1.
|
|
25
|
+
concurrent-ruby (1.1.8)
|
|
26
|
+
database_cleaner (1.99.0)
|
|
27
27
|
diff-lcs (1.4.4)
|
|
28
|
-
ffi (1.
|
|
28
|
+
ffi (1.14.2)
|
|
29
29
|
formatador (0.2.5)
|
|
30
30
|
guard (2.16.2)
|
|
31
31
|
formatador (>= 0.2.4)
|
|
@@ -41,9 +41,9 @@ GEM
|
|
|
41
41
|
guard (~> 2.1)
|
|
42
42
|
guard-compat (~> 1.1)
|
|
43
43
|
rspec (>= 2.99.0, < 4.0)
|
|
44
|
-
i18n (1.8.
|
|
44
|
+
i18n (1.8.8)
|
|
45
45
|
concurrent-ruby (~> 1.0)
|
|
46
|
-
listen (3.
|
|
46
|
+
listen (3.4.1)
|
|
47
47
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
48
48
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
49
49
|
lumberjack (1.2.8)
|
|
@@ -71,21 +71,21 @@ GEM
|
|
|
71
71
|
rspec-core (~> 3.10.0)
|
|
72
72
|
rspec-expectations (~> 3.10.0)
|
|
73
73
|
rspec-mocks (~> 3.10.0)
|
|
74
|
-
rspec-core (3.10.
|
|
74
|
+
rspec-core (3.10.1)
|
|
75
75
|
rspec-support (~> 3.10.0)
|
|
76
|
-
rspec-expectations (3.10.
|
|
76
|
+
rspec-expectations (3.10.1)
|
|
77
77
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
78
78
|
rspec-support (~> 3.10.0)
|
|
79
|
-
rspec-mocks (3.10.
|
|
79
|
+
rspec-mocks (3.10.2)
|
|
80
80
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
81
81
|
rspec-support (~> 3.10.0)
|
|
82
|
-
rspec-support (3.10.
|
|
82
|
+
rspec-support (3.10.2)
|
|
83
83
|
shellany (0.0.1)
|
|
84
|
-
thor (1.0
|
|
84
|
+
thor (1.1.0)
|
|
85
85
|
thread_safe (0.3.6)
|
|
86
86
|
tzinfo (1.2.9)
|
|
87
87
|
thread_safe (~> 0.1)
|
|
88
|
-
yard (0.9.
|
|
88
|
+
yard (0.9.26)
|
|
89
89
|
zeitwerk (2.4.2)
|
|
90
90
|
|
|
91
91
|
PLATFORMS
|
data/README.md
CHANGED
|
@@ -52,17 +52,16 @@ section of the wiki.
|
|
|
52
52
|
Installation
|
|
53
53
|
------------
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
application's Gemfile:
|
|
55
|
+
Add this line to your application's Gemfile:
|
|
57
56
|
|
|
58
57
|
```ruby
|
|
59
|
-
gem 'mobility', '~> 1.0
|
|
58
|
+
gem 'mobility', '~> 1.1.0'
|
|
60
59
|
```
|
|
61
60
|
|
|
62
61
|
### ActiveRecord (Rails)
|
|
63
62
|
|
|
64
63
|
Requirements:
|
|
65
|
-
- ActiveRecord >= 5.0 (including 6.
|
|
64
|
+
- ActiveRecord >= 5.0 (including 6.x)
|
|
66
65
|
|
|
67
66
|
(Support for most backends and features is also supported with
|
|
68
67
|
ActiveRecord/Rails 4.2, but there are some tests still failing. To see exactly
|
data/lib/mobility.rb
CHANGED
|
@@ -31,8 +31,9 @@ So the above example is equivalent to:
|
|
|
31
31
|
`Mobility.translations_class` is a subclass of `Mobility::Translations` created
|
|
32
32
|
when `Mobility.configure` is called to configure Mobility. In fact, when you
|
|
33
33
|
call `Mobility.configure`, it is the subclass of `Mobility::Translations` which
|
|
34
|
-
is passed to the block as `config
|
|
35
|
-
applied to the same
|
|
34
|
+
is passed to the block as `config` (or as `self` if no argument is passed to
|
|
35
|
+
the block). Plugins and plugin configuration is all applied to the same
|
|
36
|
+
`Mobility.translations_class`.
|
|
36
37
|
|
|
37
38
|
There is another way to use Mobility, which is to create your own subclass or
|
|
38
39
|
subclasses of +Mobility::Translations+ and include them explicitly, without
|
|
@@ -83,8 +84,6 @@ module Mobility
|
|
|
83
84
|
require "mobility/plugins"
|
|
84
85
|
require "mobility/translations"
|
|
85
86
|
|
|
86
|
-
# General error for version compatibility conflicts
|
|
87
|
-
class VersionNotSupportedError < ArgumentError; end
|
|
88
87
|
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
|
|
89
88
|
private_constant :CALL_COMPILABLE_REGEXP
|
|
90
89
|
|
|
@@ -201,6 +200,14 @@ module Mobility
|
|
|
201
200
|
end
|
|
202
201
|
end
|
|
203
202
|
|
|
203
|
+
# Check that a non-nil locale is valid. (Does not actually parse locale to
|
|
204
|
+
# check its format.)
|
|
205
|
+
# @raise [InvalidLocale] if locale is not a Symbol or not available
|
|
206
|
+
def validate_locale!(locale)
|
|
207
|
+
raise Mobility::InvalidLocale.new(locale) unless Symbol === locale
|
|
208
|
+
enforce_available_locales!(locale) if I18n.enforce_available_locales
|
|
209
|
+
end
|
|
210
|
+
|
|
204
211
|
# Raises InvalidLocale exception if the locale passed in is present but not available.
|
|
205
212
|
# @param [String,Symbol] locale
|
|
206
213
|
# @raise [InvalidLocale] if locale is present but not available
|
|
@@ -217,7 +224,7 @@ module Mobility
|
|
|
217
224
|
# simply default to +I18n.available_locales+, we may define many more
|
|
218
225
|
# methods (in LocaleAccessors) than is really necessary.
|
|
219
226
|
def available_locales
|
|
220
|
-
if defined?(Rails) && Rails.application
|
|
227
|
+
if defined?(Rails) && Rails.respond_to?(:application) && Rails.application
|
|
221
228
|
Rails.application.config.i18n.available_locales&.map(&:to_sym) || I18n.available_locales
|
|
222
229
|
else
|
|
223
230
|
I18n.available_locales
|
|
@@ -231,8 +238,8 @@ module Mobility
|
|
|
231
238
|
end
|
|
232
239
|
|
|
233
240
|
def set_locale(locale)
|
|
234
|
-
locale = locale.to_sym if locale
|
|
235
|
-
|
|
241
|
+
locale = locale.to_sym if String === locale
|
|
242
|
+
validate_locale!(locale) if locale
|
|
236
243
|
storage[:mobility_locale] = locale
|
|
237
244
|
end
|
|
238
245
|
end
|
data/lib/mobility/backend.rb
CHANGED
|
@@ -17,8 +17,10 @@ On top of this, a backend will normally:
|
|
|
17
17
|
- implement a +write+ instance method to write to the backend
|
|
18
18
|
- implement an +each_locale+ instance method to iterate through available locales
|
|
19
19
|
(used to define other +Enumerable+ traversal and search methods)
|
|
20
|
+
- implement a +valid_keys+ class method returning an array of symbols
|
|
21
|
+
corresponding to valid keys for configuring this backend.
|
|
20
22
|
- implement a +configure+ class method to apply any normalization to the
|
|
21
|
-
options hash
|
|
23
|
+
keys on the options hash included in +valid_keys+
|
|
22
24
|
- call the +setup+ method yielding attributes and options to configure the
|
|
23
25
|
model class
|
|
24
26
|
|
|
@@ -37,7 +37,6 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
37
37
|
options[:association_name] ||= :"#{options[:type]}_translations"
|
|
38
38
|
options[:class_name] ||= const_get("#{type.capitalize}Translation")
|
|
39
39
|
end
|
|
40
|
-
options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
|
|
41
40
|
rescue NameError
|
|
42
41
|
raise ArgumentError, "You must define a Mobility::Backends::ActiveRecord::KeyValue::#{type.capitalize}Translation class."
|
|
43
42
|
end
|
|
@@ -49,7 +48,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
49
48
|
# translation table value column
|
|
50
49
|
def build_node(attr, locale)
|
|
51
50
|
aliased_table = class_name.arel_table.alias(table_alias(attr, locale))
|
|
52
|
-
Plugins::Arel::Attribute.new(aliased_table,
|
|
51
|
+
Plugins::Arel::Attribute.new(aliased_table, value_column, locale, self, attr.to_sym)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
# Joins translations using either INNER/OUTER join appropriate to the query.
|
|
@@ -73,10 +72,10 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
73
72
|
m = model_class.arel_table
|
|
74
73
|
t = class_name.arel_table.alias(table_alias(key, locale))
|
|
75
74
|
relation.joins(m.join(t, join_type).
|
|
76
|
-
on(t[
|
|
75
|
+
on(t[key_column].eq(key).
|
|
77
76
|
and(t[:locale].eq(locale).
|
|
78
|
-
and(t[:
|
|
79
|
-
and(t[:
|
|
77
|
+
and(t[:"#{belongs_to}_type"].eq(model_class.base_class.name).
|
|
78
|
+
and(t[:"#{belongs_to}_id"].eq(m[:id]))))).join_sources)
|
|
80
79
|
end
|
|
81
80
|
|
|
82
81
|
def already_joined?(relation, name, locale, join_type)
|
|
@@ -151,8 +150,11 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
151
150
|
end
|
|
152
151
|
|
|
153
152
|
setup do |attributes, options|
|
|
154
|
-
association_name
|
|
155
|
-
|
|
153
|
+
association_name = options[:association_name]
|
|
154
|
+
translation_class = options[:class_name]
|
|
155
|
+
key_column = options[:key_column]
|
|
156
|
+
value_column = options[:value_column]
|
|
157
|
+
belongs_to = options[:belongs_to]
|
|
156
158
|
|
|
157
159
|
# Track all attributes for this association, so that we can limit the scope
|
|
158
160
|
# of keys for the association to only these attributes. We need to track the
|
|
@@ -163,13 +165,13 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
163
165
|
association_attributes = (instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
164
166
|
instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
165
167
|
|
|
166
|
-
has_many association_name, ->{ where
|
|
167
|
-
as:
|
|
168
|
-
class_name:
|
|
169
|
-
inverse_of:
|
|
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,
|
|
170
172
|
autosave: true
|
|
171
173
|
before_save do
|
|
172
|
-
send(association_name).select { |t| t.
|
|
174
|
+
send(association_name).select { |t| t.send(value_column).blank? }.each do |translation|
|
|
173
175
|
send(association_name).destroy(translation)
|
|
174
176
|
end
|
|
175
177
|
end
|
|
@@ -181,33 +183,32 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
|
|
|
181
183
|
super(source)
|
|
182
184
|
self.send("#{association_name}=", source.send(association_name).map(&:dup))
|
|
183
185
|
# Set inverse on associations
|
|
184
|
-
send(association_name).each
|
|
186
|
+
send(association_name).each do |translation|
|
|
187
|
+
translation.send(:"#{belongs_to}=", self)
|
|
188
|
+
end
|
|
185
189
|
end
|
|
186
190
|
end
|
|
187
191
|
include const_set(module_name, callback_methods)
|
|
188
192
|
end
|
|
189
193
|
|
|
190
|
-
|
|
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
|
|
191
201
|
end
|
|
192
202
|
|
|
193
203
|
# Returns translation for a given locale, or builds one if none is present.
|
|
194
204
|
# @param [Symbol] locale
|
|
195
205
|
# @return [Mobility::Backends::ActiveRecord::KeyValue::TextTranslation,Mobility::Backends::ActiveRecord::KeyValue::StringTranslation]
|
|
196
206
|
def translation_for(locale, **)
|
|
197
|
-
translation = translations.find
|
|
198
|
-
|
|
199
|
-
translation
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
module DestroyKeyValueTranslations
|
|
203
|
-
def self.included(model_class)
|
|
204
|
-
model_class.after_destroy do
|
|
205
|
-
[:string, :text].each do |type|
|
|
206
|
-
Mobility::Backends::ActiveRecord::KeyValue.const_get("#{type.capitalize}Translation").
|
|
207
|
-
where(translatable: self).destroy_all
|
|
208
|
-
end
|
|
209
|
-
end
|
|
207
|
+
translation = translations.find do |t|
|
|
208
|
+
t.send(key_column) == attribute && t.locale == locale.to_s
|
|
210
209
|
end
|
|
210
|
+
translation ||= translations.build(locale: locale, key_column => attribute)
|
|
211
|
+
translation
|
|
211
212
|
end
|
|
212
213
|
|
|
213
214
|
class Translation < ::ActiveRecord::Base
|
|
@@ -274,6 +274,12 @@ columns to that table.
|
|
|
274
274
|
define_method :initialize_dup do |source|
|
|
275
275
|
super(source)
|
|
276
276
|
self.send("#{association_name}=", source.send(association_name).map(&:dup))
|
|
277
|
+
|
|
278
|
+
# for cache
|
|
279
|
+
# FIXME: do this better
|
|
280
|
+
if options[:cache]
|
|
281
|
+
instance_variable_set(:"@__mobility_#{association_name}_cache", {})
|
|
282
|
+
end
|
|
277
283
|
end
|
|
278
284
|
end
|
|
279
285
|
include const_set(module_name, dupable)
|
|
@@ -55,18 +55,18 @@ other backends on model (otherwise one will overwrite the other).
|
|
|
55
55
|
# @!group Backend Accessors
|
|
56
56
|
# @!macro backend_reader
|
|
57
57
|
def read(locale, **options)
|
|
58
|
-
translation_for(locale, **options).
|
|
58
|
+
translation_for(locale, **options).send(value_column)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# @!macro backend_writer
|
|
62
62
|
def write(locale, value, **options)
|
|
63
|
-
translation_for(locale, **options).
|
|
63
|
+
translation_for(locale, **options).send(:"#{value_column}=", value)
|
|
64
64
|
end
|
|
65
65
|
# @!endgroup
|
|
66
66
|
|
|
67
67
|
# @!macro backend_iterator
|
|
68
68
|
def each_locale
|
|
69
|
-
translations.each { |t| yield(t.locale.to_sym) if t.
|
|
69
|
+
translations.each { |t| yield(t.locale.to_sym) if t.send(key_column) == attribute }
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
private
|
|
@@ -79,12 +79,14 @@ other backends on model (otherwise one will overwrite the other).
|
|
|
79
79
|
backend_class.extend ClassMethods
|
|
80
80
|
backend_class.option_reader :association_name
|
|
81
81
|
backend_class.option_reader :class_name
|
|
82
|
-
backend_class.option_reader :
|
|
82
|
+
backend_class.option_reader :key_column
|
|
83
|
+
backend_class.option_reader :value_column
|
|
84
|
+
backend_class.option_reader :belongs_to
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
module ClassMethods
|
|
86
88
|
def valid_keys
|
|
87
|
-
[:type, :association_name, :class_name]
|
|
89
|
+
[:type, :association_name, :class_name, :key_column, :value_column, :belongs_to]
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
# @!group Backend Configuration
|
|
@@ -99,6 +101,9 @@ other backends on model (otherwise one will overwrite the other).
|
|
|
99
101
|
options[:type] &&= options[:type].to_sym
|
|
100
102
|
options[:association_name] &&= options[:association_name].to_sym
|
|
101
103
|
options[:class_name] &&= Util.constantize(options[:class_name])
|
|
104
|
+
options[:key_column] ||= :key
|
|
105
|
+
options[:value_column] ||= :value
|
|
106
|
+
options[:belongs_to] ||= :translatable
|
|
102
107
|
if !(options[:type] || (options[:class_name] && options[:association_name]))
|
|
103
108
|
raise ArgumentError, "KeyValue backend requires an explicit type option, either text or string."
|
|
104
109
|
end
|
|
@@ -110,7 +115,7 @@ other backends on model (otherwise one will overwrite the other).
|
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
def table_alias(attr, locale)
|
|
113
|
-
|
|
118
|
+
"#{model_class}_#{attr}_#{Mobility.normalize_locale(locale)}_#{options[:association_name]}"
|
|
114
119
|
end
|
|
115
120
|
end
|
|
116
121
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
|
+
require "set"
|
|
2
3
|
require "mobility/util"
|
|
3
4
|
require "mobility/backends/sequel"
|
|
4
5
|
require "mobility/backends/key_value"
|
|
@@ -31,14 +32,13 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
31
32
|
options[:association_name] ||= :"#{options[:type]}_translations"
|
|
32
33
|
options[:class_name] ||= const_get("#{type.capitalize}Translation")
|
|
33
34
|
end
|
|
34
|
-
options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
|
|
35
35
|
rescue NameError
|
|
36
36
|
raise ArgumentError, "You must define a Mobility::Sequel::#{type.capitalize}Translation class."
|
|
37
37
|
end
|
|
38
38
|
# @!endgroup
|
|
39
39
|
|
|
40
40
|
def build_op(attr, locale)
|
|
41
|
-
QualifiedIdentifier.new(table_alias(attr, locale),
|
|
41
|
+
QualifiedIdentifier.new(table_alias(attr, locale), value_column, locale, self, attr)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# @param [Sequel::Dataset] dataset Dataset to prepare
|
|
@@ -57,10 +57,10 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
57
57
|
dataset.join_table(join_type,
|
|
58
58
|
class_name.table_name,
|
|
59
59
|
{
|
|
60
|
-
|
|
61
|
-
locale
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
key_column => attr.to_s,
|
|
61
|
+
:locale => locale.to_s,
|
|
62
|
+
:"#{belongs_to}_type" => model_class.name,
|
|
63
|
+
:"#{belongs_to}_id" => ::Sequel[:"#{model_class.table_name}"][:id]
|
|
64
64
|
},
|
|
65
65
|
table_alias: table_alias(attr, locale))
|
|
66
66
|
end
|
|
@@ -126,27 +126,37 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
126
126
|
backend = self
|
|
127
127
|
|
|
128
128
|
setup do |attributes, options|
|
|
129
|
-
association_name
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
association_name = options[:association_name]
|
|
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
|
+
#
|
|
132
142
|
attrs_method_name = :"#{association_name}_attributes"
|
|
133
143
|
association_attributes = (instance_variable_get(:"@#{attrs_method_name}") || []) + attributes
|
|
134
144
|
instance_variable_set(:"@#{attrs_method_name}", association_attributes)
|
|
135
145
|
|
|
136
146
|
one_to_many association_name,
|
|
137
|
-
reciprocal:
|
|
138
|
-
key:
|
|
147
|
+
reciprocal: belongs_to,
|
|
148
|
+
key: belongs_to_id,
|
|
139
149
|
reciprocal_type: :one_to_many,
|
|
140
|
-
conditions: {
|
|
141
|
-
adder: proc { |translation| translation.update(
|
|
142
|
-
remover: proc { |translation| translation.update(
|
|
143
|
-
clearer: proc {
|
|
144
|
-
class:
|
|
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
|
|
145
155
|
|
|
146
156
|
callback_methods = Module.new do
|
|
147
157
|
define_method :before_save do
|
|
148
158
|
super()
|
|
149
|
-
send(association_name).select { |t| attributes.include?(t.
|
|
159
|
+
send(association_name).select { |t| attributes.include?(t.__send__(key_column)) && Util.blank?(t.__send__(value_column)) }.each(&:destroy)
|
|
150
160
|
end
|
|
151
161
|
define_method :after_save do
|
|
152
162
|
super()
|
|
@@ -155,7 +165,17 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
155
165
|
end
|
|
156
166
|
include callback_methods
|
|
157
167
|
|
|
158
|
-
|
|
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()
|
|
172
|
+
|
|
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
|
|
159
179
|
include(mod = Module.new)
|
|
160
180
|
backend.define_column_changes(mod, attributes)
|
|
161
181
|
end
|
|
@@ -164,30 +184,19 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
164
184
|
# @param [Symbol] locale
|
|
165
185
|
# @return [Mobility::Backends::Sequel::KeyValue::TextTranslation,Mobility::Backends::Sequel::KeyValue::StringTranslation]
|
|
166
186
|
def translation_for(locale, **)
|
|
167
|
-
translation = model.send(association_name).find { |t| t.
|
|
168
|
-
translation ||= class_name.new(locale: locale,
|
|
187
|
+
translation = model.send(association_name).find { |t| t.__send__(key_column) == attribute && t.locale == locale.to_s }
|
|
188
|
+
translation ||= class_name.new(locale: locale, key_column => attribute)
|
|
169
189
|
translation
|
|
170
190
|
end
|
|
171
191
|
|
|
172
192
|
# Saves translation which have been built and which have non-blank values.
|
|
173
193
|
def save_translations
|
|
174
194
|
cache.each_value do |translation|
|
|
175
|
-
next unless present?(translation.
|
|
195
|
+
next unless present?(translation.__send__ value_column)
|
|
176
196
|
translation.id ? translation.save : model.send("add_#{singularize(association_name)}", translation)
|
|
177
197
|
end
|
|
178
198
|
end
|
|
179
199
|
|
|
180
|
-
# Clean up *all* leftover translations of this model, only once.
|
|
181
|
-
module DestroyKeyValueTranslations
|
|
182
|
-
def after_destroy
|
|
183
|
-
super
|
|
184
|
-
[:string, :text].freeze.each do |type|
|
|
185
|
-
Mobility::Backends::Sequel::KeyValue.const_get("#{type.capitalize}Translation").
|
|
186
|
-
where(translatable_id: id, translatable_type: self.class.name).destroy
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
200
|
class CacheRequired < ::StandardError; end
|
|
192
201
|
|
|
193
202
|
module Cache
|
|
@@ -211,8 +220,32 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
211
220
|
end
|
|
212
221
|
end
|
|
213
222
|
|
|
214
|
-
|
|
215
|
-
|
|
223
|
+
class Translatable < Module
|
|
224
|
+
attr_reader :key_column, :value_column, :belongs_to, :id_column, :type_column
|
|
225
|
+
|
|
226
|
+
def initialize(key_column, value_column, belongs_to)
|
|
227
|
+
@key_column = key_column
|
|
228
|
+
@value_column = value_column
|
|
229
|
+
@belongs_to = belongs_to
|
|
230
|
+
@id_column = :"#{belongs_to}_id"
|
|
231
|
+
@type_column = :"#{belongs_to}_type"
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Strictly these are not "descendants", but to keep terminology
|
|
235
|
+
# consistent with ActiveRecord KeyValue backend.
|
|
236
|
+
def descendants
|
|
237
|
+
@descendants ||= Set.new
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def included(base)
|
|
241
|
+
@descendants ||= Set.new
|
|
242
|
+
@descendants << base
|
|
243
|
+
|
|
244
|
+
mod = self
|
|
245
|
+
key_column = mod.key_column
|
|
246
|
+
id_column = mod.id_column
|
|
247
|
+
type_column = mod.type_column
|
|
248
|
+
|
|
216
249
|
base.class_eval do
|
|
217
250
|
plugin :validation_helpers
|
|
218
251
|
|
|
@@ -220,16 +253,16 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
220
253
|
#
|
|
221
254
|
model = underscore(self.to_s)
|
|
222
255
|
plural_model = pluralize(model)
|
|
223
|
-
many_to_one
|
|
256
|
+
many_to_one mod.belongs_to,
|
|
224
257
|
reciprocal: plural_model.to_sym,
|
|
225
258
|
reciprocal_type: :many_to_one,
|
|
226
259
|
setter: (proc do |able_instance|
|
|
227
|
-
self[
|
|
228
|
-
self[
|
|
260
|
+
self[id_column] = (able_instance.pk if able_instance)
|
|
261
|
+
self[type_column] = (able_instance.class.name if able_instance)
|
|
229
262
|
end),
|
|
230
263
|
dataset: (proc do
|
|
231
|
-
translatable_type = send
|
|
232
|
-
translatable_id = send
|
|
264
|
+
translatable_type = send type_column
|
|
265
|
+
translatable_id = send id_column
|
|
233
266
|
return if translatable_type.nil? || translatable_id.nil?
|
|
234
267
|
klass = self.class.send(:constantize, translatable_type)
|
|
235
268
|
klass.where(klass.primary_key => translatable_id)
|
|
@@ -237,29 +270,30 @@ Implements the {Mobility::Backends::KeyValue} backend for Sequel models.
|
|
|
237
270
|
eager_loader: (proc do |eo|
|
|
238
271
|
id_map = {}
|
|
239
272
|
eo[:rows].each do |model|
|
|
240
|
-
model_able_type = model.send
|
|
241
|
-
model_able_id = model.send
|
|
242
|
-
model.associations[
|
|
273
|
+
model_able_type = model.send type_column
|
|
274
|
+
model_able_id = model.send id_column
|
|
275
|
+
model.associations[belongs_to] = nil
|
|
243
276
|
((id_map[model_able_type] ||= {})[model_able_id] ||= []) << model if !model_able_type.nil? && !model_able_id.nil?
|
|
244
277
|
end
|
|
245
278
|
id_map.each do |klass_name, id_map|
|
|
246
279
|
klass = constantize(camelize(klass_name))
|
|
247
280
|
klass.where(klass.primary_key=>id_map.keys).all do |related_obj|
|
|
248
281
|
id_map[related_obj.pk].each do |model|
|
|
249
|
-
model.associations[
|
|
282
|
+
model.associations[belongs_to] = related_obj
|
|
250
283
|
end
|
|
251
284
|
end
|
|
252
285
|
end
|
|
253
286
|
end)
|
|
254
287
|
|
|
255
|
-
|
|
256
|
-
super
|
|
257
|
-
validates_presence [:locale,
|
|
258
|
-
validates_unique [:locale,
|
|
288
|
+
define_method :validate do
|
|
289
|
+
super()
|
|
290
|
+
validates_presence [:locale, key_column, id_column, type_column]
|
|
291
|
+
validates_unique [:locale, key_column, id_column, type_column]
|
|
259
292
|
end
|
|
260
293
|
end
|
|
261
294
|
end
|
|
262
295
|
end
|
|
296
|
+
Translation = Translatable.new(:key, :value, :translatable)
|
|
263
297
|
|
|
264
298
|
class TextTranslation < ::Sequel::Model(:mobility_text_translations)
|
|
265
299
|
include Translation
|
data/lib/mobility/plugin.rb
CHANGED
|
@@ -8,7 +8,9 @@ require_relative "./active_record/uniqueness_validation"
|
|
|
8
8
|
module Mobility
|
|
9
9
|
=begin
|
|
10
10
|
|
|
11
|
-
Plugin for ActiveRecord models.
|
|
11
|
+
Plugin for ActiveRecord models. This plugin automatically requires activerecord
|
|
12
|
+
related plugins, which are not actually "active" unless their base plugin (e.g.
|
|
13
|
+
dirty for active_record_dirty) is also enabled.
|
|
12
14
|
|
|
13
15
|
=end
|
|
14
16
|
module Plugins
|
|
@@ -29,7 +29,9 @@ enabled for any one attribute on the model.
|
|
|
29
29
|
klass.class_eval do
|
|
30
30
|
extend QueryMethod
|
|
31
31
|
extend FindByMethods.new(*plugin.names)
|
|
32
|
-
singleton_class.
|
|
32
|
+
singleton_class.define_method(plugin.query_method) do |locale: Mobility.locale, &block|
|
|
33
|
+
Query.build_query(self, locale, &block)
|
|
34
|
+
end
|
|
33
35
|
end
|
|
34
36
|
backend_class.include BackendMethods
|
|
35
37
|
end
|
|
@@ -39,6 +41,14 @@ enabled for any one attribute on the model.
|
|
|
39
41
|
def attribute_alias(attribute, locale = Mobility.locale)
|
|
40
42
|
"__mobility_%s_%s__" % [attribute, ::Mobility.normalize_locale(locale)]
|
|
41
43
|
end
|
|
44
|
+
|
|
45
|
+
def build_query(klass, locale = Mobility.locale, &block)
|
|
46
|
+
if block_given?
|
|
47
|
+
VirtualRow.build_query(klass, locale, &block)
|
|
48
|
+
else
|
|
49
|
+
klass.all.extending(QueryExtension)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
42
52
|
end
|
|
43
53
|
|
|
44
54
|
module BackendMethods
|
|
@@ -57,13 +67,9 @@ enabled for any one attribute on the model.
|
|
|
57
67
|
end
|
|
58
68
|
|
|
59
69
|
module QueryMethod
|
|
60
|
-
# This is required for UniquenessValidator.
|
|
61
70
|
def __mobility_query_scope__(locale: Mobility.locale, &block)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
else
|
|
65
|
-
all.extending(QueryExtension)
|
|
66
|
-
end
|
|
71
|
+
warn '__mobility_query_scope__ is an internal method and will be deprecated in the next release.'
|
|
72
|
+
Query.build_query(self, locale, &block)
|
|
67
73
|
end
|
|
68
74
|
end
|
|
69
75
|
|
|
@@ -71,18 +77,21 @@ enabled for any one attribute on the model.
|
|
|
71
77
|
# an instance-eval'ed block. Inspired by Sequel's (much more
|
|
72
78
|
# sophisticated) virtual rows.
|
|
73
79
|
class VirtualRow < BasicObject
|
|
74
|
-
attr_reader :
|
|
80
|
+
attr_reader :backends, :locales
|
|
75
81
|
|
|
76
|
-
def initialize(
|
|
77
|
-
@
|
|
82
|
+
def initialize(klass, global_locale)
|
|
83
|
+
@klass, @global_locale, @locales, @backends = klass, global_locale, [], []
|
|
78
84
|
end
|
|
79
85
|
|
|
80
|
-
def method_missing(m, *)
|
|
81
|
-
if @
|
|
82
|
-
@
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@
|
|
86
|
+
def method_missing(m, *args)
|
|
87
|
+
if @klass.mobility_attribute?(m)
|
|
88
|
+
@backends |= [@klass.mobility_backend_class(m)]
|
|
89
|
+
::Mobility.validate_locale!(args[0]) if args[0]
|
|
90
|
+
locale = args[0] || @global_locale
|
|
91
|
+
@locales |= [locale]
|
|
92
|
+
@klass.mobility_backend_class(m).build_node(m, locale)
|
|
93
|
+
elsif @klass.column_names.include?(m.to_s)
|
|
94
|
+
@klass.arel_table[m]
|
|
86
95
|
else
|
|
87
96
|
super
|
|
88
97
|
end
|
|
@@ -90,21 +99,27 @@ enabled for any one attribute on the model.
|
|
|
90
99
|
|
|
91
100
|
class << self
|
|
92
101
|
def build_query(klass, locale, &block)
|
|
102
|
+
::Mobility.validate_locale!(locale)
|
|
103
|
+
|
|
93
104
|
row = new(klass, locale)
|
|
94
105
|
query = block.arity.zero? ? row.instance_eval(&block) : block.call(row)
|
|
95
106
|
|
|
96
107
|
if ::ActiveRecord::Relation === query
|
|
97
108
|
predicates = query.arel.constraints
|
|
98
|
-
apply_scopes(klass.all, row.
|
|
109
|
+
apply_scopes(klass.all, row.backends, row.locales, predicates).merge(query)
|
|
99
110
|
else
|
|
100
|
-
apply_scopes(klass.all, row.
|
|
111
|
+
apply_scopes(klass.all, row.backends, row.locales, query).where(query)
|
|
101
112
|
end
|
|
102
113
|
end
|
|
103
114
|
|
|
104
115
|
private
|
|
105
116
|
|
|
106
|
-
def apply_scopes(scope, backends,
|
|
107
|
-
backends.inject(scope)
|
|
117
|
+
def apply_scopes(scope, backends, locales, predicates)
|
|
118
|
+
backends.inject(scope) do |scope_, b|
|
|
119
|
+
locales.inject(scope_) do |r, locale|
|
|
120
|
+
b.apply_scope(r, predicates, locale)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
108
123
|
end
|
|
109
124
|
end
|
|
110
125
|
end
|
|
@@ -27,7 +27,7 @@ module Mobility
|
|
|
27
27
|
|
|
28
28
|
if ([*options[:scope]] + [attribute]).any? { |name| klass.mobility_attribute?(name) }
|
|
29
29
|
return unless value.present?
|
|
30
|
-
relation = klass.unscoped.
|
|
30
|
+
relation = Plugins::ActiveRecord::Query.build_query(klass.unscoped, Mobility.locale) do |m|
|
|
31
31
|
node = m.__send__(attribute)
|
|
32
32
|
options[:case_sensitive] == false ? node.lower.eq(value.downcase) : node.eq(value)
|
|
33
33
|
end
|
|
@@ -50,7 +50,7 @@ module Mobility
|
|
|
50
50
|
|
|
51
51
|
def mobility_scope_relation(record, relation)
|
|
52
52
|
[*options[:scope]].inject(relation) do |scoped_relation, scope_item|
|
|
53
|
-
scoped_relation.
|
|
53
|
+
Plugins::ActiveRecord::Query.build_query(scoped_relation, Mobility.locale) do |m|
|
|
54
54
|
m.__send__(scope_item).eq(record.send(scope_item))
|
|
55
55
|
end
|
|
56
56
|
end
|
|
@@ -19,12 +19,6 @@ for aggregating attributes.
|
|
|
19
19
|
@names = names.map(&:to_s).freeze
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
# Yield each attribute name to block
|
|
23
|
-
# @yieldparam [String] Attribute
|
|
24
|
-
def each &block
|
|
25
|
-
names.each(&block)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
22
|
# Show useful information about this module.
|
|
29
23
|
# @return [String]
|
|
30
24
|
def inspect
|
|
@@ -33,13 +33,6 @@ Defines:
|
|
|
33
33
|
def initialize(*args, **original_options)
|
|
34
34
|
super
|
|
35
35
|
|
|
36
|
-
# Validate that the default backend from config has valid keys
|
|
37
|
-
if (default = self.class.defaults[:backend])
|
|
38
|
-
name, backend_options = default
|
|
39
|
-
extra_keys = backend_options.keys - backend.valid_keys
|
|
40
|
-
raise InvalidOptionKey, "These are not valid #{name} backend keys: #{extra_keys.join(', ')}." unless extra_keys.empty?
|
|
41
|
-
end
|
|
42
|
-
|
|
43
36
|
include InstanceMethods
|
|
44
37
|
end
|
|
45
38
|
|
|
@@ -87,10 +80,10 @@ Defines:
|
|
|
87
80
|
|
|
88
81
|
case options[:backend]
|
|
89
82
|
when String, Symbol, Class
|
|
90
|
-
@backend, @backend_options = options[:backend], options
|
|
83
|
+
@backend, @backend_options = options[:backend], options.dup
|
|
91
84
|
when Array
|
|
92
85
|
@backend, @backend_options = options[:backend]
|
|
93
|
-
@backend_options = @backend_options.merge(
|
|
86
|
+
@backend_options = @backend_options.merge(options)
|
|
94
87
|
when NilClass
|
|
95
88
|
@backend = @backend_options = nil
|
|
96
89
|
else
|
|
@@ -105,6 +98,14 @@ Defines:
|
|
|
105
98
|
def validate_options(options)
|
|
106
99
|
return super unless backend
|
|
107
100
|
super(options.slice(*(options.keys - backend.valid_keys)))
|
|
101
|
+
|
|
102
|
+
# Validate that the default backend from config has valid keys, or if
|
|
103
|
+
# it is overridden by an array input that the array has valid keys.
|
|
104
|
+
if options[:backend].is_a?(Array)
|
|
105
|
+
name, backend_options = options[:backend]
|
|
106
|
+
extra_keys = backend_options.keys - backend.valid_keys
|
|
107
|
+
raise InvalidOptionKey, "These are not valid #{name} backend keys: #{extra_keys.join(', ')}." unless extra_keys.empty?
|
|
108
|
+
end
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
# Override default argument-handling in DSL to store kwargs passed along
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require "sequel"
|
|
2
|
-
raise VersionNotSupportedError, "Mobility is only compatible with Sequel 4.0 and greater" if ::Sequel::MAJOR < 4
|
|
3
2
|
require "sequel/plugins/mobility"
|
|
4
3
|
unless defined?(ActiveSupport::Inflector)
|
|
5
4
|
# TODO: avoid automatically including the inflector extension
|
|
@@ -13,6 +12,13 @@ require_relative "./sequel/query"
|
|
|
13
12
|
|
|
14
13
|
module Mobility
|
|
15
14
|
module Plugins
|
|
15
|
+
=begin
|
|
16
|
+
|
|
17
|
+
Plugin for Sequel models. This plugin automatically requires sequel related
|
|
18
|
+
plugins, which are not actually "active" unless their base plugin (e.g. dirty
|
|
19
|
+
for sequel_dirty) is also enabled.
|
|
20
|
+
|
|
21
|
+
=end
|
|
16
22
|
module Sequel
|
|
17
23
|
extend Plugin
|
|
18
24
|
|
|
@@ -3,7 +3,8 @@ module Mobility
|
|
|
3
3
|
module Plugins
|
|
4
4
|
=begin
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Supports querying on Sequel model translated attributes. Similar API to the
|
|
7
|
+
ActiveRecord query plugin.
|
|
7
8
|
|
|
8
9
|
=end
|
|
9
10
|
module Sequel
|
|
@@ -19,35 +20,47 @@ See ActiveRecord::Query plugin.
|
|
|
19
20
|
|
|
20
21
|
klass.class_eval do
|
|
21
22
|
extend QueryMethod
|
|
22
|
-
singleton_class.
|
|
23
|
+
singleton_class.define_method(plugin.query_method) do |locale: Mobility.locale, &block|
|
|
24
|
+
Query.build_query(self, locale, &block)
|
|
25
|
+
end
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
end
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
def
|
|
30
|
+
class << self
|
|
31
|
+
def build_query(klass, locale = Mobility.locale, &block)
|
|
29
32
|
if block_given?
|
|
30
|
-
VirtualRow.build_query(
|
|
33
|
+
VirtualRow.build_query(klass, locale, &block)
|
|
31
34
|
else
|
|
32
|
-
dataset.with_extend(QueryExtension)
|
|
35
|
+
klass.dataset.with_extend(QueryExtension)
|
|
33
36
|
end
|
|
34
37
|
end
|
|
35
38
|
end
|
|
36
39
|
|
|
40
|
+
module QueryMethod
|
|
41
|
+
def __mobility_query_dataset__(locale: Mobility.locale, &block)
|
|
42
|
+
warn '__mobility_query_dataset__ is an internal method and will be deprecated in the next release.'
|
|
43
|
+
Query.build_query(self, locale, &block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
37
47
|
# Internal class to create a "clean room" for manipulating translated
|
|
38
48
|
# attribute nodes in an instance-eval'ed block. Inspired by Sequel's
|
|
39
49
|
# (much more sophisticated) virtual rows.
|
|
40
50
|
class VirtualRow < BasicObject
|
|
41
|
-
attr_reader :
|
|
51
|
+
attr_reader :backends, :locales
|
|
42
52
|
|
|
43
|
-
def initialize(model_class,
|
|
44
|
-
@model_class, @
|
|
53
|
+
def initialize(model_class, global_locale)
|
|
54
|
+
@model_class, @global_locale, @backends, @locales = model_class, global_locale, [], []
|
|
45
55
|
end
|
|
46
56
|
|
|
47
|
-
def method_missing(m, *)
|
|
57
|
+
def method_missing(m, *args)
|
|
48
58
|
if @model_class.mobility_attribute?(m)
|
|
49
|
-
@
|
|
50
|
-
|
|
59
|
+
@backends |= [@model_class.mobility_backend_class(m)]
|
|
60
|
+
::Mobility.validate_locale!(args[0]) if args[0]
|
|
61
|
+
locale = args[0] || @global_locale
|
|
62
|
+
@locales |= [locale]
|
|
63
|
+
@model_class.mobility_backend_class(m).build_op(m.to_s, locale)
|
|
51
64
|
elsif @model_class.columns.include?(m.to_s)
|
|
52
65
|
::Sequel::SQL::QualifiedIdentifier.new(@model_class.table_name, m)
|
|
53
66
|
else
|
|
@@ -57,21 +70,27 @@ See ActiveRecord::Query plugin.
|
|
|
57
70
|
|
|
58
71
|
class << self
|
|
59
72
|
def build_query(klass, locale, &block)
|
|
73
|
+
::Mobility.validate_locale!(locale)
|
|
74
|
+
|
|
60
75
|
row = new(klass, locale)
|
|
61
76
|
query = block.arity.zero? ? row.instance_eval(&block) : block.call(row)
|
|
62
77
|
|
|
63
78
|
if ::Sequel::Dataset === query
|
|
64
79
|
predicates = query.opts[:where]
|
|
65
|
-
prepare_datasets(query, row.
|
|
80
|
+
prepare_datasets(query, row.backends, row.locales, predicates)
|
|
66
81
|
else
|
|
67
|
-
prepare_datasets(klass.dataset, row.
|
|
82
|
+
prepare_datasets(klass.dataset, row.backends, row.locales, query).where(query)
|
|
68
83
|
end
|
|
69
84
|
end
|
|
70
85
|
|
|
71
86
|
private
|
|
72
87
|
|
|
73
|
-
def prepare_datasets(dataset, backends,
|
|
74
|
-
backends.inject(dataset)
|
|
88
|
+
def prepare_datasets(dataset, backends, locales, predicates)
|
|
89
|
+
backends.inject(dataset) do |dataset_, b|
|
|
90
|
+
locales.inject(dataset_) do |ds, locale|
|
|
91
|
+
b.prepare_dataset(ds, predicates, locale)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
75
94
|
end
|
|
76
95
|
end
|
|
77
96
|
end
|
data/lib/mobility/version.rb
CHANGED
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.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Salzberg
|
|
@@ -34,7 +34,7 @@ cert_chain:
|
|
|
34
34
|
gSQml7TqcC6dZRsZRwYqzD9kUwdAJoCqno2CBUKs2l0yQAjFT36lRrVJznb7uWwa
|
|
35
35
|
xpPFnsrtyaZW6Dty8TSG3qzmeGpmpIotA8x1VA==
|
|
36
36
|
-----END CERTIFICATE-----
|
|
37
|
-
date: 2021-
|
|
37
|
+
date: 2021-02-06 00:00:00.000000000 Z
|
|
38
38
|
dependencies:
|
|
39
39
|
- !ruby/object:Gem::Dependency
|
|
40
40
|
name: request_store
|
metadata.gz.sig
CHANGED
|
Binary file
|