mobility 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -12
- data/Gemfile.lock +17 -17
- data/README.md +22 -7
- data/lib/generators/{mobility → rails/mobility}/install_generator.rb +0 -0
- data/lib/generators/{mobility → rails/mobility}/templates/create_string_translations.rb +2 -1
- data/lib/generators/{mobility → rails/mobility}/templates/create_text_translations.rb +1 -1
- data/lib/mobility.rb +1 -2
- data/lib/mobility/backend.rb +2 -3
- data/lib/mobility/backend/active_model/dirty.rb +1 -1
- data/lib/mobility/backend/active_record/column.rb +1 -1
- data/lib/mobility/backend/active_record/key_value.rb +4 -7
- data/lib/mobility/backend/active_record/key_value/query_methods.rb +38 -29
- data/lib/mobility/backend/active_record/table/query_methods.rb +40 -33
- data/lib/mobility/backend/fallbacks.rb +25 -11
- data/lib/mobility/backend/key_value.rb +14 -0
- data/lib/mobility/backend/sequel/column.rb +1 -1
- data/lib/mobility/backend/sequel/dirty.rb +2 -1
- data/lib/mobility/backend/sequel/key_value.rb +5 -8
- data/lib/mobility/backend/sequel/key_value/query_methods.rb +20 -9
- data/lib/mobility/backend/sequel/serialized.rb +7 -7
- data/lib/mobility/backend/sequel/table/query_methods.rb +17 -14
- data/lib/mobility/rails.rb +2 -0
- data/lib/mobility/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c83db6c82d88d2972c328ea414426416b57aee7
|
4
|
+
data.tar.gz: b058744cc114446d2d641046b3d14cbc843eef8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d44c71b7381046f0fc63ea7cb7aed5506aa2860324dced1a6d58c07d3b1aa9fe9c10d337d17d42e4807a722a78faca46b2e386611b4e2a984176daaf90efdd3
|
7
|
+
data.tar.gz: 3e29ad8f1a11cb343291dbe0fa63633433e2463a40e70b85834898f808064b85fb129fba59db7588471743e4ee90c2bf276a8f50f90b8c9993cc2b1870d39107
|
data/CHANGELOG.md
CHANGED
@@ -2,30 +2,47 @@
|
|
2
2
|
|
3
3
|
## 0.1
|
4
4
|
|
5
|
+
### 0.1.7
|
6
|
+
* Allow passing fallback locale or locales to getter method
|
7
|
+
([#9](https://github.com/shioyama/mobility/pull/9))
|
8
|
+
* Add missing indices on key-value string/text translation tables
|
9
|
+
([1e00e0](https://github.com/shioyama/mobility/commit/1e00e0d957478f2408fbac1ee853f829489263e2),
|
10
|
+
[574172](https://github.com/shioyama/mobility/commit/574172dc88823a35c60ff963ff9c40b7c05771d7))
|
11
|
+
|
12
|
+
### 0.1.6
|
13
|
+
* Return accessor locales instead of Proc from default_accessor_locales
|
14
|
+
([825f75](https://github.com/shioyama/mobility/commit/825f75de6107287a5de70db439d8aec5e4a47977))
|
15
|
+
* Fix support for locales in dirty modules
|
16
|
+
([0b40d6](https://github.com/shioyama/mobility/commit/0b40d66ea0c816d4fb57deceff9344f5128a593f))
|
17
|
+
* Add FallthroughAccessors for use in dirty modules
|
18
|
+
([#4](https://github.com/shioyama/mobility/pull/4))
|
19
|
+
* Only raise InvalidLocale exception if I18n.enforce_available_locales is true
|
20
|
+
([979c36](https://github.com/shioyama/mobility/commit/979c365794d3df90a2d23ad50519ff354686a493))
|
21
|
+
|
5
22
|
### 0.1.5
|
6
|
-
* Add `accessor_method` to default initializer ([
|
7
|
-
* Include AR version in generated migrations ([
|
8
|
-
* Add `untranslated_attributes` method ([
|
9
|
-
* Do not require `active_support/core_ext/nil` ([
|
10
|
-
* Handle false values correctly when getting and setting ([
|
11
|
-
* Use proc to define accessor locales from `I18n.available_locales` ([
|
23
|
+
* Add `accessor_method` to default initializer ([d4a9da](https://github.com/shioyama/mobility/commit/d4a9da98cae71de2fb9ee3d29c64decef5a16010))
|
24
|
+
* Include AR version in generated migrations ([ac3dfb](https://github.com/shioyama/mobility/commit/ac3dfbbc053089b01dcc73d0b617fefaeaaa85cb))
|
25
|
+
* Add `untranslated_attributes` method ([50e97f](https://github.com/shioyama/mobility/commit/50e97f12ea219321ef9f61792e909299f570ba23))
|
26
|
+
* Do not require `active_support/core_ext/nil` ([39e245](https://github.com/shioyama/mobility/commit/39e24596482f03302542e524ca6f17275a778644))
|
27
|
+
* Handle false values correctly when getting and setting ([bdf6f1](https://github.com/shioyama/mobility/commit/bdf6f199aaa8318a73c5aa6332aee8d7aad254f6))
|
28
|
+
* Use proc to define accessor locales from `I18n.available_locales` ([3cd786](https://github.com/shioyama/mobility/commit/3cd786814d8044ae5d64f939c3a7b5c49b322bc6))
|
12
29
|
* Do not mark attribute as changed if value is the same (fixed in [#2](https://github.com/shioyama/mobility/pull/2))
|
13
30
|
* Pass on any args to original reload method when overriding (fixed in [#3](https://github.com/shioyama/mobility/pull/3))
|
14
31
|
|
15
32
|
### 0.1.4
|
16
|
-
* Fix configuration reload issue ([#1](https://github.com/shioyama/mobility/issues/1), fixed in [
|
17
|
-
* Code refactoring/cleanup ([
|
18
|
-
* Allow using Sequel `plugin` to include Mobility in model ([
|
33
|
+
* Fix configuration reload issue ([#1](https://github.com/shioyama/mobility/issues/1), fixed in [478b66](https://github.com/shioyama/mobility/commit/478b669dae90edf9feb7c011ae93e8157dc4e2b4))
|
34
|
+
* Code refactoring/cleanup ([e4dcc7](https://github.com/shioyama/mobility/commit/e4dcc791c246e377352b9ac154d2b1c4aec8e98e), [64f434](https://github.com/shioyama/mobility/commit/64f434ea7a46c9353c3638c58a3258f0fcb81821), [8df2bb](https://github.com/shioyama/mobility/commit/8df2bbdead883725d2c87020f836b644b4d28e5c), [326a09](https://github.com/shioyama/mobility/commit/326a0977c98348dad85a927c20dd69fe5acb2a9e))
|
35
|
+
* Allow using Sequel `plugin` to include Mobility in model ([b0db7c](https://github.com/shioyama/mobility/commit/b0db7cc28a47e13c6888ef263260e8dff281543d))
|
19
36
|
|
20
37
|
### 0.1.3
|
21
38
|
|
22
39
|
* Add homepage to gemspec
|
23
40
|
* Pass backend class as context to `translates`
|
24
|
-
([
|
41
|
+
([adf93e](https://github.com/shioyama/mobility/commit/adf93e3c6bb314b73fbd43b221819310a1407c4d))
|
25
42
|
|
26
43
|
### 0.1.2
|
27
44
|
|
28
45
|
* Fix issues with querying in ActiveRecord jsonb and hstore backends
|
29
|
-
([
|
46
|
+
([527908](https://github.com/shioyama/mobility/commit/527908d9317daee6bf91e3e1a188fb64365f7bab)
|
30
47
|
and
|
31
|
-
[
|
48
|
+
[5e6add](https://github.com/shioyama/mobility/commit/5e6addd6f01cf255f5e71666324502ace96d3eac))
|
data/Gemfile.lock
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
mobility (0.1.
|
4
|
+
mobility (0.1.6)
|
5
5
|
i18n (>= 0.6.10)
|
6
6
|
request_store (~> 1.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
actionpack (5.0.
|
12
|
-
actionview (= 5.0.
|
13
|
-
activesupport (= 5.0.
|
11
|
+
actionpack (5.0.2)
|
12
|
+
actionview (= 5.0.2)
|
13
|
+
activesupport (= 5.0.2)
|
14
14
|
rack (~> 2.0)
|
15
15
|
rack-test (~> 0.6.3)
|
16
16
|
rails-dom-testing (~> 2.0)
|
17
17
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
18
|
-
actionview (5.0.
|
19
|
-
activesupport (= 5.0.
|
18
|
+
actionview (5.0.2)
|
19
|
+
activesupport (= 5.0.2)
|
20
20
|
builder (~> 3.1)
|
21
21
|
erubis (~> 2.7.0)
|
22
22
|
rails-dom-testing (~> 2.0)
|
23
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.
|
24
|
-
activemodel (5.0.
|
25
|
-
activesupport (= 5.0.
|
26
|
-
activerecord (5.0.
|
27
|
-
activemodel (= 5.0.
|
28
|
-
activesupport (= 5.0.
|
23
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
24
|
+
activemodel (5.0.2)
|
25
|
+
activesupport (= 5.0.2)
|
26
|
+
activerecord (5.0.2)
|
27
|
+
activemodel (= 5.0.2)
|
28
|
+
activesupport (= 5.0.2)
|
29
29
|
arel (~> 7.0)
|
30
|
-
activesupport (5.0.
|
30
|
+
activesupport (5.0.2)
|
31
31
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
32
32
|
i18n (~> 0.7)
|
33
33
|
minitest (~> 5.1)
|
@@ -36,7 +36,7 @@ GEM
|
|
36
36
|
builder (3.2.3)
|
37
37
|
byebug (9.0.6)
|
38
38
|
coderay (1.1.1)
|
39
|
-
concurrent-ruby (1.0.
|
39
|
+
concurrent-ruby (1.0.5)
|
40
40
|
database_cleaner (1.5.3)
|
41
41
|
diff-lcs (1.3)
|
42
42
|
erubis (2.7.0)
|
@@ -92,9 +92,9 @@ GEM
|
|
92
92
|
nokogiri (~> 1.6)
|
93
93
|
rails-html-sanitizer (1.0.3)
|
94
94
|
loofah (~> 2.0)
|
95
|
-
railties (5.0.
|
96
|
-
actionpack (= 5.0.
|
97
|
-
activesupport (= 5.0.
|
95
|
+
railties (5.0.2)
|
96
|
+
actionpack (= 5.0.2)
|
97
|
+
activesupport (= 5.0.2)
|
98
98
|
method_source
|
99
99
|
rake (>= 0.8.7)
|
100
100
|
thor (>= 0.18.1, < 2.0)
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ Mobility](http://dejimata.com/2017/3/3/translating-with-mobility).
|
|
56
56
|
Add this line to your application's Gemfile:
|
57
57
|
|
58
58
|
```ruby
|
59
|
-
gem 'mobility', '~> 0.1.
|
59
|
+
gem 'mobility', '~> 0.1.7'
|
60
60
|
```
|
61
61
|
|
62
62
|
To translate attributes on a model, you must include (or extend) `Mobility`,
|
@@ -386,7 +386,7 @@ Alternatively, just using `locale_accessors: true` will enable all locales in
|
|
386
386
|
|
387
387
|
An alternative to using the `locale_accessors` option is to use the
|
388
388
|
`fallthrough_accessors` option (defined in {Mobility::FallthroughAccessors})
|
389
|
-
with `fallthrough_accessors: true`. This uses
|
389
|
+
with `fallthrough_accessors: true`. This uses `method_missing` to implicitly
|
390
390
|
define the same methods as above, but supporting any locale without any method
|
391
391
|
definitions. [Dirty tracking](#dirty) enables fallthrough locales for tracking
|
392
392
|
attribute changes. (Both locale accessors and fallthrough locales can be used
|
@@ -427,6 +427,9 @@ class Post < ActiveRecord::Base
|
|
427
427
|
end
|
428
428
|
```
|
429
429
|
|
430
|
+
Internally, Mobility assigns the fallbacks hash to an instance of
|
431
|
+
`I18n::Locale::Fallbacks.new`.
|
432
|
+
|
430
433
|
By setting fallbacks for English and French to Japanese, values will fall
|
431
434
|
through to the Japanese value if none is present for either of these locales:
|
432
435
|
|
@@ -445,17 +448,29 @@ post.title_fr
|
|
445
448
|
|
446
449
|
You can optionally disable fallbacks to get the real value for a given locale
|
447
450
|
(for example, to check if a value in a particular locale is set or not) by
|
448
|
-
passing `
|
451
|
+
passing `fallback: false` (note that the key is the *singular*, not plural) to
|
452
|
+
the getter method:
|
449
453
|
|
450
454
|
```ruby
|
451
|
-
post.title(
|
455
|
+
post.title(fallback: false)
|
452
456
|
#=> nil
|
453
|
-
post.
|
457
|
+
post.title(locale: :fr, fallback: false)
|
454
458
|
#=> nil
|
455
459
|
```
|
456
460
|
|
457
|
-
|
458
|
-
|
461
|
+
You can also set the fallback locales for a single read by passing one or more
|
462
|
+
locales, like this:
|
463
|
+
|
464
|
+
```ruby
|
465
|
+
post.title_fr = "mobilité: aptitude à bouger, à se déplacer, à changer, à évoluer"
|
466
|
+
post.save
|
467
|
+
post.title
|
468
|
+
#=> nil
|
469
|
+
post.title(fallback: :fr)
|
470
|
+
#=> "mobilité: aptitude à bouger, à se déplacer, à changer, à évoluer"
|
471
|
+
post.title(fallback: [:ja, :fr])
|
472
|
+
#=> "Mobility(名詞):動きやすさ、可動性"
|
473
|
+
```
|
459
474
|
|
460
475
|
For more details, see: {Mobility::Backend::Fallbacks}.
|
461
476
|
|
File without changes
|
@@ -10,6 +10,7 @@ class CreateStringTranslations < ActiveRecord::Migration[<%= ActiveRecord::Migra
|
|
10
10
|
t.timestamps
|
11
11
|
end
|
12
12
|
add_index :mobility_string_translations, [:translatable_id, :translatable_type, :locale, :key], unique: true, name: :index_mobility_string_translations_on_keys
|
13
|
-
add_index :mobility_string_translations, [:translatable_id, :translatable_type], name: :
|
13
|
+
add_index :mobility_string_translations, [:translatable_id, :translatable_type, :key], name: :index_mobility_string_translations_on_translatable_attribute
|
14
|
+
add_index :mobility_string_translations, [:translatable_type, :key, :value, :locale], name: :index_mobility_string_translations_on_query_keys
|
14
15
|
end
|
15
16
|
end
|
@@ -10,6 +10,6 @@ class CreateTextTranslations < ActiveRecord::Migration[<%= ActiveRecord::Migrati
|
|
10
10
|
t.timestamps
|
11
11
|
end
|
12
12
|
add_index :mobility_text_translations, [:translatable_id, :translatable_type, :locale, :key], unique: true, name: :index_mobility_text_translations_on_keys
|
13
|
-
add_index :mobility_text_translations, [:translatable_id, :translatable_type], name: :
|
13
|
+
add_index :mobility_text_translations, [:translatable_id, :translatable_type, :key], name: :index_mobility_text_translations_on_translatable_attribute
|
14
14
|
end
|
15
15
|
end
|
data/lib/mobility.rb
CHANGED
@@ -58,10 +58,9 @@ module Mobility
|
|
58
58
|
|
59
59
|
begin
|
60
60
|
require "rails"
|
61
|
-
autoload :InstallGenerator, "generators/mobility/install_generator"
|
62
61
|
Loaded::Rails = true
|
62
|
+
require "mobility/rails"
|
63
63
|
rescue LoadError
|
64
|
-
class InstallGenerator; end
|
65
64
|
Loaded::Rails = false
|
66
65
|
end
|
67
66
|
|
data/lib/mobility/backend.rb
CHANGED
@@ -76,12 +76,11 @@ On top of this, a backend will normally:
|
|
76
76
|
# @!macro [new] backend_constructor
|
77
77
|
# @param model Model on which backend is defined
|
78
78
|
# @param [String] attribute Backend attribute
|
79
|
-
# @
|
80
|
-
def initialize(model, attribute, **options)
|
79
|
+
# @param [Hash] fallbacks Fallbacks hash
|
80
|
+
def initialize(model, attribute, fallbacks: nil, **options)
|
81
81
|
@model = model
|
82
82
|
@attribute = attribute
|
83
83
|
@options = options
|
84
|
-
fallbacks = options[:fallbacks]
|
85
84
|
@fallbacks = I18n::Locale::Fallbacks.new(fallbacks) if fallbacks.is_a?(Hash)
|
86
85
|
end
|
87
86
|
|
@@ -28,7 +28,7 @@ value of the translated attribute if passed to it.
|
|
28
28
|
locale_accessor = Mobility.normalize_locale_accessor(attribute, locale)
|
29
29
|
if model.changed_attributes.has_key?(locale_accessor) && model.changed_attributes[locale_accessor] == value
|
30
30
|
model.attributes_changed_by_setter.except!(locale_accessor)
|
31
|
-
elsif read(locale, options.merge(
|
31
|
+
elsif read(locale, options.merge(fallback: false)) != value
|
32
32
|
model.send(:attribute_will_change!, locale_accessor)
|
33
33
|
end
|
34
34
|
super
|
@@ -21,7 +21,7 @@ Implements the {Mobility::Backend::Column} backend for ActiveRecord models.
|
|
21
21
|
=end
|
22
22
|
class ActiveRecord::Column
|
23
23
|
include Backend
|
24
|
-
include
|
24
|
+
include Backend::Column
|
25
25
|
|
26
26
|
autoload :QueryMethods, 'mobility/backend/active_record/column/query_methods'
|
27
27
|
|
@@ -20,6 +20,7 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
|
|
20
20
|
=end
|
21
21
|
class ActiveRecord::KeyValue
|
22
22
|
include Backend
|
23
|
+
include Backend::KeyValue
|
23
24
|
|
24
25
|
autoload :QueryMethods, 'mobility/backend/active_record/key_value/query_methods'
|
25
26
|
|
@@ -51,13 +52,9 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
|
|
51
52
|
# @option options [String,Class] class_name ({Mobility::ActiveRecord::TextTranslation}) Translation class
|
52
53
|
# @raise [ArgumentError] if type is not either :text or :string
|
53
54
|
def self.configure!(options)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
|
58
|
-
else
|
59
|
-
raise ArgumentError, "type must be one of: [text, string]"
|
60
|
-
end
|
55
|
+
super
|
56
|
+
type = options[:type]
|
57
|
+
options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
|
61
58
|
options[:class_name] = options[:class_name].constantize if options[:class_name].is_a?(String)
|
62
59
|
options[:association_name] ||= options[:class_name].table_name.to_sym
|
63
60
|
%i[type association_name].each { |key| options[key] = options[key].to_sym }
|
@@ -1,37 +1,12 @@
|
|
1
1
|
module Mobility
|
2
2
|
module Backend
|
3
3
|
class ActiveRecord::KeyValue::QueryMethods < ActiveRecord::QueryMethods
|
4
|
-
def initialize(attributes, **
|
4
|
+
def initialize(attributes, association_name: nil, class_name: nil, **_)
|
5
5
|
super
|
6
|
-
association_name
|
7
|
-
@association_name = association_name
|
8
|
-
attributes_extractor = @attributes_extractor
|
9
|
-
|
10
|
-
define_method :"join_#{association_name}" do |*attributes, **options|
|
11
|
-
attributes.inject(self) do |relation, attribute|
|
12
|
-
t = translations_class.arel_table.alias(:"#{attribute}_#{association_name}")
|
13
|
-
m = arel_table
|
14
|
-
join_type = options[:outer_join] ? Arel::Nodes::OuterJoin : Arel::Nodes::InnerJoin
|
15
|
-
relation.joins(m.join(t, join_type).
|
16
|
-
on(t[:key].eq(attribute).
|
17
|
-
and(t[:locale].eq(Mobility.locale).
|
18
|
-
and(t[:translatable_type].eq(name).
|
19
|
-
and(t[:translatable_id].eq(m[:id]))))).join_sources)
|
20
|
-
end
|
21
|
-
end
|
6
|
+
@association_name = association_name
|
22
7
|
|
23
|
-
|
24
|
-
|
25
|
-
opts = opts.with_indifferent_access
|
26
|
-
i18n_nulls = i18n_keys.select { |key| opts[key].nil? }
|
27
|
-
i18n_keys.each { |attr| opts["#{attr}_#{association_name}"] = { value: opts.delete(attr) }}
|
28
|
-
super(opts, *rest).
|
29
|
-
send("join_#{association_name}", *(i18n_keys - i18n_nulls)).
|
30
|
-
send("join_#{association_name}", *i18n_nulls, outer_join: true)
|
31
|
-
else
|
32
|
-
super(opts, *rest)
|
33
|
-
end
|
34
|
-
end
|
8
|
+
define_join_method(association_name, class_name)
|
9
|
+
define_query_methods(association_name)
|
35
10
|
|
36
11
|
attributes.each do |attribute|
|
37
12
|
define_method :"find_by_#{attribute}" do |value|
|
@@ -58,6 +33,40 @@ module Mobility
|
|
58
33
|
end
|
59
34
|
relation.model.mobility_where_chain.prepend(mod)
|
60
35
|
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def define_join_method(association_name, translation_class)
|
40
|
+
define_method :"join_#{association_name}" do |*attributes, **options|
|
41
|
+
attributes.inject(self) do |relation, attribute|
|
42
|
+
t = translation_class.arel_table.alias(:"#{attribute}_#{association_name}")
|
43
|
+
m = arel_table
|
44
|
+
join_type = options[:outer_join] ? Arel::Nodes::OuterJoin : Arel::Nodes::InnerJoin
|
45
|
+
relation.joins(m.join(t, join_type).
|
46
|
+
on(t[:key].eq(attribute).
|
47
|
+
and(t[:locale].eq(Mobility.locale).
|
48
|
+
and(t[:translatable_type].eq(name).
|
49
|
+
and(t[:translatable_id].eq(m[:id]))))).join_sources)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def define_query_methods(association_name)
|
55
|
+
attributes_extractor = @attributes_extractor
|
56
|
+
|
57
|
+
define_method :where! do |opts, *rest|
|
58
|
+
if i18n_keys = attributes_extractor.call(opts)
|
59
|
+
opts = opts.with_indifferent_access
|
60
|
+
i18n_nulls = i18n_keys.select { |key| opts[key].nil? }
|
61
|
+
i18n_keys.each { |attr| opts["#{attr}_#{association_name}"] = { value: opts.delete(attr) }}
|
62
|
+
super(opts, *rest).
|
63
|
+
send("join_#{association_name}", *(i18n_keys - i18n_nulls)).
|
64
|
+
send("join_#{association_name}", *i18n_nulls, outer_join: true)
|
65
|
+
else
|
66
|
+
super(opts, *rest)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
63
72
|
end
|
@@ -1,16 +1,45 @@
|
|
1
1
|
module Mobility
|
2
2
|
module Backend
|
3
3
|
class ActiveRecord::Table::QueryMethods < ActiveRecord::QueryMethods
|
4
|
-
def initialize(attributes, **options)
|
4
|
+
def initialize(attributes, association_name: nil, model_class: nil, subclass_name: nil, **options)
|
5
5
|
super
|
6
|
-
|
7
|
-
|
8
|
-
@
|
6
|
+
|
7
|
+
@association_name = association_name
|
8
|
+
@translation_class = translation_class = model_class.const_get(subclass_name)
|
9
|
+
|
10
|
+
define_join_method(association_name, translation_class, **options)
|
11
|
+
define_query_methods(association_name, translation_class, **options)
|
12
|
+
|
13
|
+
attributes.each do |attribute|
|
14
|
+
define_method :"find_by_#{attribute}" do |value|
|
15
|
+
find_by(attribute.to_sym => value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def extended(relation)
|
21
|
+
super
|
22
|
+
association_name = @association_name
|
9
23
|
attributes_extractor = @attributes_extractor
|
10
|
-
translation_class =
|
11
|
-
|
12
|
-
|
24
|
+
translation_class = @translation_class
|
25
|
+
|
26
|
+
mod = Module.new do
|
27
|
+
define_method :not do |opts, *rest|
|
28
|
+
if i18n_keys = attributes_extractor.call(opts)
|
29
|
+
opts = opts.with_indifferent_access
|
30
|
+
i18n_keys.each { |attr| opts["#{translation_class.table_name}.#{attr}"] = opts.delete(attr) }
|
31
|
+
super(opts, *rest).send("join_#{association_name}")
|
32
|
+
else
|
33
|
+
super(opts, *rest)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
relation.model.mobility_where_chain.prepend(mod)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
13
41
|
|
42
|
+
def define_join_method(association_name, translation_class, foreign_key: nil, table_name: nil, **_)
|
14
43
|
define_method :"join_#{association_name}" do |**options|
|
15
44
|
return self if (@__mobility_table_joined || []).include?(table_name)
|
16
45
|
(@__mobility_table_joined ||= []) << table_name
|
@@ -21,6 +50,10 @@ module Mobility
|
|
21
50
|
on(t[foreign_key].eq(m[:id]).
|
22
51
|
and(t[:locale].eq(Mobility.locale))).join_sources)
|
23
52
|
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def define_query_methods(association_name, translation_class, **_)
|
56
|
+
attributes_extractor = @attributes_extractor
|
24
57
|
|
25
58
|
# Note that Mobility will try to use inner/outer joins appropriate to the query,
|
26
59
|
# so for example:
|
@@ -59,32 +92,6 @@ module Mobility
|
|
59
92
|
super(opts, *rest)
|
60
93
|
end
|
61
94
|
end
|
62
|
-
|
63
|
-
attributes.each do |attribute|
|
64
|
-
define_method :"find_by_#{attribute}" do |value|
|
65
|
-
find_by(attribute.to_sym => value)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def extended(relation)
|
71
|
-
super
|
72
|
-
association_name = @association_name
|
73
|
-
attributes_extractor = @attributes_extractor
|
74
|
-
translation_class = @translation_class
|
75
|
-
|
76
|
-
mod = Module.new do
|
77
|
-
define_method :not do |opts, *rest|
|
78
|
-
if i18n_keys = attributes_extractor.call(opts)
|
79
|
-
opts = opts.with_indifferent_access
|
80
|
-
i18n_keys.each { |attr| opts["#{translation_class.table_name}.#{attr}"] = opts.delete(attr) }
|
81
|
-
super(opts, *rest).send("join_#{association_name}")
|
82
|
-
else
|
83
|
-
super(opts, *rest)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
relation.model.mobility_where_chain.prepend(mod)
|
88
95
|
end
|
89
96
|
end
|
90
97
|
end
|
@@ -13,9 +13,14 @@ defaults to an instance of +I18n::Locale::Fallbacks+, but can be configured
|
|
13
13
|
If a hash is passed to the +fallbacks+ option, a new fallbacks instance will be
|
14
14
|
created for the model with the hash defining additional fallbacks.
|
15
15
|
|
16
|
-
In addition, fallbacks can be disabled when reading by passing
|
17
|
-
false
|
18
|
-
of the translated attribute, including a possible +nil+ value.
|
16
|
+
In addition, fallbacks can be disabled when reading by passing <tt>fallback:
|
17
|
+
false</tt> to the reader method. This can be useful to determine the actual
|
18
|
+
value of the translated attribute, including a possible +nil+ value. You can
|
19
|
+
also pass a locale or array of locales to the +fallback+ option to use that
|
20
|
+
locale or locales that read, e.g. <tt>fallback: :fr</tt> would fetch the French
|
21
|
+
translation if the value in the current locale was +nil+, whereas <tt>fallback:
|
22
|
+
[:fr, :es]</tt> would try French, then Spanish if the value in the current
|
23
|
+
locale was +nil+.
|
19
24
|
|
20
25
|
@see https://github.com/svenfuchs/i18n/wiki/Fallbacks I18n Fallbacks
|
21
26
|
|
@@ -52,28 +57,37 @@ of the translated attribute, including a possible +nil+ value.
|
|
52
57
|
post.title
|
53
58
|
#=> "bar"
|
54
59
|
|
55
|
-
@example
|
60
|
+
@example Passing fallback option when reading value
|
56
61
|
class Post
|
57
62
|
translates :title, fallbacks: true
|
58
63
|
end
|
59
64
|
|
60
65
|
I18n.default_locale = :en
|
61
66
|
Mobility.locale = :en
|
62
|
-
post = Post.new(title: "
|
67
|
+
post = Post.new(title: "Mobility")
|
68
|
+
Mobility.with_locale(:fr) { post.title = "Mobilité" }
|
63
69
|
|
64
70
|
Mobility.locale = :ja
|
65
71
|
post.title
|
66
|
-
#=> "
|
67
|
-
post.title(
|
72
|
+
#=> "Mobility"
|
73
|
+
post.title(fallback: false)
|
68
74
|
#=> nil
|
75
|
+
post.title(fallback: :fr)
|
76
|
+
#=> "Mobilité"
|
69
77
|
=end
|
70
78
|
module Fallbacks
|
71
79
|
# @!group Backend Accessors
|
72
80
|
# @!macro backend_reader
|
73
|
-
# @
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
# @param [Boolean,Symbol,Array] fallback
|
82
|
+
# +false+ to disable fallbacks on lookup, or a locale or array of
|
83
|
+
# locales to set fallback(s) for this lookup.
|
84
|
+
def read(locale, fallback: nil, **_)
|
85
|
+
if !options[:fallbacks].nil?
|
86
|
+
warn "You passed an option with key 'fallbacks', which will be
|
87
|
+
ignored. Did you mean 'fallback'?"
|
88
|
+
end
|
89
|
+
return super if fallback == false
|
90
|
+
(fallback ? [locale, *fallback] : fallbacks[locale]).detect do |locale|
|
77
91
|
value = super(locale)
|
78
92
|
break value if value.present?
|
79
93
|
end
|
@@ -41,6 +41,20 @@ class.
|
|
41
41
|
module KeyValue
|
42
42
|
include OrmDelegator
|
43
43
|
|
44
|
+
def self.included(backend)
|
45
|
+
backend.extend ClassMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
# @!group Backend Configuration
|
50
|
+
# @option options [Symbol,String] type (:text) Column type to use
|
51
|
+
# @raise [ArgumentError] if type is not either :text or :string
|
52
|
+
def configure!(options)
|
53
|
+
options[:type] = (options[:type] || :text).to_sym
|
54
|
+
raise ArgumentError, "type must be one of: [text, string]" unless [:text, :string].include?(options[:type])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
44
58
|
# Simple cache to memoize translations as a hash so they can be fetched
|
45
59
|
# quickly.
|
46
60
|
class TranslationsCache
|
@@ -9,7 +9,7 @@ Implements the {Mobility::Backend::Column} backend for Sequel models.
|
|
9
9
|
=end
|
10
10
|
class Sequel::Column
|
11
11
|
include Backend
|
12
|
-
include
|
12
|
+
include Backend::Column
|
13
13
|
|
14
14
|
autoload :QueryMethods, 'mobility/backend/sequel/column/query_methods'
|
15
15
|
|
@@ -11,12 +11,13 @@ Automatically includes dirty plugin in model class when enabled.
|
|
11
11
|
module Sequel::Dirty
|
12
12
|
# @!group Backend Accessors
|
13
13
|
# @!macro backend_writer
|
14
|
+
# @param [Hash] options
|
14
15
|
def write(locale, value, **options)
|
15
16
|
locale_accessor = Mobility.normalize_locale_accessor(attribute, locale).to_sym
|
16
17
|
if model.column_changes.has_key?(locale_accessor) && model.initial_values[locale_accessor] == value
|
17
18
|
super
|
18
19
|
[model.changed_columns, model.initial_values].each { |h| h.delete(locale_accessor) }
|
19
|
-
elsif read(locale, options.merge(
|
20
|
+
elsif read(locale, options.merge(fallback: false)) != value
|
20
21
|
model.will_change_column(locale_accessor)
|
21
22
|
super
|
22
23
|
end
|
@@ -11,6 +11,7 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
|
|
11
11
|
=end
|
12
12
|
class Sequel::KeyValue
|
13
13
|
include Backend
|
14
|
+
include Backend::KeyValue
|
14
15
|
|
15
16
|
autoload :QueryMethods, 'mobility/backend/sequel/key_value/query_methods'
|
16
17
|
|
@@ -42,20 +43,16 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
|
|
42
43
|
# @!endgroup
|
43
44
|
|
44
45
|
# @!group Backend Configuration
|
45
|
-
# @option options [Symbol] type (:text) Column type to use
|
46
|
+
# @option options [Symbol,String] type (:text) Column type to use
|
46
47
|
# @option options [Symbol] associaiton_name (:mobility_text_translations) Name of association method
|
47
48
|
# @option options [Symbol] class_name ({Mobility::Sequel::TextTranslation}) Translation class
|
48
49
|
# @raise [CacheRequired] if cache is disabled
|
49
50
|
# @raise [ArgumentError] if type is not either :text or :string
|
50
51
|
def self.configure!(options)
|
52
|
+
super
|
51
53
|
raise CacheRequired, "Cache required for Sequel::KeyValue backend" if options[:cache] == false
|
52
|
-
options[:type]
|
53
|
-
|
54
|
-
when :text, :string
|
55
|
-
options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation")
|
56
|
-
else
|
57
|
-
raise ArgumentError, "type must be one of: [text, string]"
|
58
|
-
end
|
54
|
+
type = options[:type]
|
55
|
+
options[:class_name] ||= Mobility::Sequel.const_get("#{type.capitalize}Translation")
|
59
56
|
options[:class_name] = options[:class_name].constantize if options[:class_name].is_a?(String)
|
60
57
|
options[:association_name] ||= options[:class_name].table_name.to_sym
|
61
58
|
%i[type association_name].each { |key| options[key] = options[key].to_sym }
|
@@ -1,16 +1,27 @@
|
|
1
1
|
module Mobility
|
2
2
|
module Backend
|
3
3
|
class Sequel::KeyValue::QueryMethods < Sequel::QueryMethods
|
4
|
-
def initialize(attributes, **
|
4
|
+
def initialize(attributes, association_name: nil, class_name: nil, **_)
|
5
5
|
super
|
6
|
-
attributes_extractor = @attributes_extractor
|
7
|
-
association_name, translations_class = options[:association_name], options[:class_name]
|
8
6
|
|
7
|
+
define_join_method(association_name, class_name)
|
8
|
+
define_query_methods(association_name)
|
9
|
+
|
10
|
+
attributes.each do |attribute|
|
11
|
+
define_method :"first_by_#{attribute}" do |value|
|
12
|
+
where(attribute => value).select_all(model.table_name).first
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def define_join_method(association_name, translation_class)
|
9
20
|
define_method :"join_#{association_name}" do |*attributes, **options|
|
10
21
|
attributes.inject(self) do |relation, attribute|
|
11
22
|
join_type = options[:outer_join] ? :left_outer : :inner
|
12
23
|
relation.join_table(join_type,
|
13
|
-
|
24
|
+
translation_class.table_name,
|
14
25
|
{
|
15
26
|
key: attribute.to_s,
|
16
27
|
locale: Mobility.locale.to_s,
|
@@ -20,9 +31,14 @@ module Mobility
|
|
20
31
|
table_alias: "#{attribute}_#{association_name}")
|
21
32
|
end
|
22
33
|
end
|
34
|
+
end
|
23
35
|
|
36
|
+
def define_query_methods(association_name)
|
24
37
|
# TODO: find a better way to do this that doesn't involve overriding
|
25
38
|
# a private method...
|
39
|
+
#
|
40
|
+
attributes_extractor = @attributes_extractor
|
41
|
+
|
26
42
|
define_method :_filter_or_exclude do |invert, clause, *cond, &block|
|
27
43
|
if i18n_keys = attributes_extractor.call(cond.first)
|
28
44
|
cond = cond.first.dup
|
@@ -37,11 +53,6 @@ module Mobility
|
|
37
53
|
end
|
38
54
|
private :_filter_or_exclude
|
39
55
|
|
40
|
-
attributes.each do |attribute|
|
41
|
-
define_method :"first_by_#{attribute}" do |value|
|
42
|
-
where(attribute => value).select_all(model.table_name).first
|
43
|
-
end
|
44
|
-
end
|
45
56
|
end
|
46
57
|
end
|
47
58
|
end
|
@@ -60,8 +60,8 @@ Sequel serialization plugin.
|
|
60
60
|
plugin :serialization
|
61
61
|
plugin :serialization_modification_detection
|
62
62
|
|
63
|
-
attributes.each do |
|
64
|
-
attribute =
|
63
|
+
attributes.each do |attribute_|
|
64
|
+
attribute = attribute_.to_sym
|
65
65
|
self.serialization_map[attribute] = Serialized.serializer_for(format)
|
66
66
|
self.deserialization_map[attribute] = Serialized.deserializer_for(format)
|
67
67
|
end
|
@@ -87,13 +87,13 @@ Sequel serialization plugin.
|
|
87
87
|
# Returns deserialized column value
|
88
88
|
# @return [Hash]
|
89
89
|
def translations
|
90
|
-
|
91
|
-
if model.deserialized_values.has_key?(
|
92
|
-
model.deserialized_values[
|
90
|
+
attribute_ = attribute.to_sym
|
91
|
+
if model.deserialized_values.has_key?(attribute_)
|
92
|
+
model.deserialized_values[attribute_]
|
93
93
|
elsif model.frozen?
|
94
|
-
deserialize_value(
|
94
|
+
deserialize_value(attribute_, serialized_value)
|
95
95
|
else
|
96
|
-
model.deserialized_values[
|
96
|
+
model.deserialized_values[attribute_] = deserialize_value(attribute_, serialized_value)
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
@@ -1,16 +1,21 @@
|
|
1
1
|
module Mobility
|
2
2
|
module Backend
|
3
3
|
class Sequel::Table::QueryMethods < Sequel::QueryMethods
|
4
|
-
def initialize(attributes, **options)
|
4
|
+
def initialize(attributes, association_name: nil, model_class: nil, subclass_name: nil, **options)
|
5
5
|
super
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
6
|
+
translation_class = model_class.const_get(subclass_name)
|
7
|
+
|
8
|
+
define_join_method(association_name, translation_class, **options)
|
9
|
+
define_query_methods(association_name, translation_class, **options)
|
10
|
+
|
11
|
+
attributes.each do |attribute|
|
12
|
+
define_method :"first_by_#{attribute}" do |value|
|
13
|
+
where(attribute => value).select_all(model.table_name).first
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
13
17
|
|
18
|
+
def define_join_method(association_name, translation_class, table_name: nil, foreign_key: nil, **_)
|
14
19
|
define_method :"join_#{association_name}" do |**options|
|
15
20
|
return self if (@__mobility_table_joined || []).include?(table_name)
|
16
21
|
(@__mobility_table_joined ||= []) << table_name
|
@@ -22,6 +27,10 @@ module Mobility
|
|
22
27
|
foreign_key => ::Sequel[model.table_name][:id]
|
23
28
|
})
|
24
29
|
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_query_methods(association_name, translation_class, **_)
|
33
|
+
attributes_extractor = @attributes_extractor
|
25
34
|
|
26
35
|
# See note in AR Table QueryMethods class about limitations of
|
27
36
|
# query methods on translated attributes when searching on nil values.
|
@@ -36,12 +45,6 @@ module Mobility
|
|
36
45
|
super(invert, clause, *cond, &block)
|
37
46
|
end
|
38
47
|
end
|
39
|
-
|
40
|
-
attributes.each do |attribute|
|
41
|
-
define_method :"first_by_#{attribute}" do |value|
|
42
|
-
where(attribute => value).select_all(model.table_name).first
|
43
|
-
end
|
44
|
-
end
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
data/lib/mobility/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobility
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Salzberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: request_store
|
@@ -137,9 +137,9 @@ files:
|
|
137
137
|
- LICENSE.txt
|
138
138
|
- README.md
|
139
139
|
- Rakefile
|
140
|
-
- lib/generators/mobility/install_generator.rb
|
141
|
-
- lib/generators/mobility/templates/create_string_translations.rb
|
142
|
-
- lib/generators/mobility/templates/create_text_translations.rb
|
140
|
+
- lib/generators/rails/mobility/install_generator.rb
|
141
|
+
- lib/generators/rails/mobility/templates/create_string_translations.rb
|
142
|
+
- lib/generators/rails/mobility/templates/create_text_translations.rb
|
143
143
|
- lib/mobility.rb
|
144
144
|
- lib/mobility/active_model.rb
|
145
145
|
- lib/mobility/active_model/attribute_methods.rb
|
@@ -203,6 +203,7 @@ files:
|
|
203
203
|
- lib/mobility/fallthrough_accessors.rb
|
204
204
|
- lib/mobility/instance_methods.rb
|
205
205
|
- lib/mobility/orm.rb
|
206
|
+
- lib/mobility/rails.rb
|
206
207
|
- lib/mobility/sequel.rb
|
207
208
|
- lib/mobility/sequel/backend_resetter.rb
|
208
209
|
- lib/mobility/sequel/column_changes.rb
|