mobility 0.1.5 → 0.1.6

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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +16 -8
  4. data/lib/mobility/attributes.rb +19 -8
  5. data/lib/mobility/backend/active_model/dirty.rb +8 -5
  6. data/lib/mobility/backend/active_record/column.rb +1 -1
  7. data/lib/mobility/backend/active_record/hash_valued.rb +2 -2
  8. data/lib/mobility/backend/active_record/hstore.rb +1 -1
  9. data/lib/mobility/backend/active_record/key_value.rb +3 -3
  10. data/lib/mobility/backend/active_record/query_methods.rb +1 -2
  11. data/lib/mobility/backend/active_record/serialized.rb +2 -2
  12. data/lib/mobility/backend/active_record/table.rb +4 -4
  13. data/lib/mobility/backend/cache.rb +3 -4
  14. data/lib/mobility/backend/column.rb +3 -3
  15. data/lib/mobility/backend/sequel/column.rb +1 -1
  16. data/lib/mobility/backend/sequel/dirty.rb +6 -4
  17. data/lib/mobility/backend/sequel/hash_valued.rb +2 -2
  18. data/lib/mobility/backend/sequel/hstore.rb +1 -1
  19. data/lib/mobility/backend/sequel/key_value.rb +2 -2
  20. data/lib/mobility/backend/sequel/query_methods.rb +1 -2
  21. data/lib/mobility/backend/sequel/serialized.rb +2 -2
  22. data/lib/mobility/backend/sequel/table.rb +3 -3
  23. data/lib/mobility/backend.rb +0 -2
  24. data/lib/mobility/configuration.rb +10 -3
  25. data/lib/mobility/fallthrough_accessors.rb +57 -0
  26. data/lib/mobility/sequel/column_changes.rb +1 -1
  27. data/lib/mobility/version.rb +1 -1
  28. data/lib/mobility.rb +29 -11
  29. metadata +3 -5
  30. data/lib/generators/mobility/templates/translations.rb +0 -46
  31. data/lib/generators/mobility/translations_generator.rb +0 -20
  32. data/lib/mobility/backend/i18n.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02ff52251ec982025bdc93821a706fce97ddfb5b
4
- data.tar.gz: 00cc021046e222050f8baea42bdf436d93f689b3
3
+ metadata.gz: e672887874e0e97c1862c468003721bcabfecb34
4
+ data.tar.gz: afc6154a1bb62a9e3a633ea7b65a31856441ad9a
5
5
  SHA512:
6
- metadata.gz: 9b65346069fb65cb2b90fa311ce72c1c50122f30319da89e5752d72c61295bca4c83aca798e256824b324b97e1c87e9e93ef3b38faed4e9f528329eab02c47e2
7
- data.tar.gz: 14f9257baed55a7de39b53b37dc3f6c07e3c415ee0f088af78ca6e548c6939b97f5c495a20966a9e39263b27f6280ceaec304843f0920c542f45bfce86b7d1e0
6
+ metadata.gz: 5c461d32a74de20c33103be3f038443edf0768e25fca9f654d555fbeaa2b8e1fbf7587cf8b2a39264bc602cd26278929110eb3ac71d68c2f3dda8fb3c0cbfbca
7
+ data.tar.gz: e59f35065d9be082c7ecb33ec2af3097b9e782b3ae3822749cb4df38fbfb0766927171b8b171f18746b64ecb477ebaa97aecc815c8117096b9d6da8f74d2b42c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mobility (0.1.4)
4
+ mobility (0.1.5)
5
5
  i18n (>= 0.6.10)
6
6
  request_store (~> 1.0)
7
7
 
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.5'
59
+ gem 'mobility', '~> 0.1.6'
60
60
  ```
61
61
 
62
62
  To translate attributes on a model, you must include (or extend) `Mobility`,
@@ -169,8 +169,8 @@ The options hash is used to generate the backend, and has several reserved keys:
169
169
  Enable [fallbacks](#fallbacks), and optionally configure them.
170
170
  - **`dirty`** (Boolean)<br>
171
171
  Whether to enable [dirty tracking](#dirty).
172
- - **`accessor_locales`** (Boolean or Array)<br>
173
- Enable [locale accessors](#locale_accessors) and optionally configure them.
172
+ - **`locale_accessors`** (Boolean or Array)<br>
173
+ Enable [locale accessors](#locale-accessors) and optionally configure them.
174
174
 
175
175
  In addition to these, each backend may have specific configuration options. For
176
176
  example, the default key-value backend, which stores attributes and their
@@ -384,6 +384,14 @@ post.title
384
384
  Alternatively, just using `locale_accessors: true` will enable all locales in
385
385
  `I18n.available_locales`.
386
386
 
387
+ An alternative to using the `locale_accessors` option is to use the
388
+ `fallthrough_accessors` option (defined in {Mobility::FallthroughAccessors})
389
+ with `fallthrough_accessors: true`. This uses +method_missing+ to implicitly
390
+ define the same methods as above, but supporting any locale without any method
391
+ definitions. [Dirty tracking](#dirty) enables fallthrough locales for tracking
392
+ attribute changes. (Both locale accessors and fallthrough locales can be used
393
+ together without conflict.)
394
+
387
395
  For more details, see: {Mobility::Attributes} (specifically, the private method
388
396
  `define_locale_accessors`).
389
397
 
@@ -465,11 +473,11 @@ dirty tracking is not specific to AR and works for non-persisted models as well)
465
473
  ```ruby
466
474
  class Post < ActiveRecord::Base
467
475
  include Mobility
468
- translates :title, locale_accessors: [:en, :ja], dirty: true
476
+ translates :title, dirty: true
469
477
  end
470
478
  ```
471
479
 
472
- Now set the attribute in both locales:
480
+ Now set the attribute in two locales:
473
481
 
474
482
  ```ruby
475
483
  post.title
@@ -512,9 +520,9 @@ post.previous_changes
512
520
  }
513
521
  ```
514
522
 
515
- You will notice that Mobility uses locale accessors to indicate which locale
516
- has changed; dirty tracking is implemented this way to ensure that it is clear
517
- what has changed in which locale, avoiding any possible ambiguity.
523
+ You will notice that Mobility uses locale accessor methods to indicate which
524
+ locale has changed; dirty tracking is implemented this way to ensure that it is
525
+ clear what has changed in which locale, avoiding any possible ambiguity.
518
526
 
519
527
  For more details, see: {Mobility::Backend::Dirty}.
520
528
 
@@ -119,8 +119,12 @@ with other backends.
119
119
  # accessors or specify locales for which accessors should be defined on
120
120
  # this model backend. Will default to +true+ if +dirty+ option is +true+.
121
121
  # @option options_ [Boolean] cache (true) Enable cache for this model backend
122
- # @option options_ [Boolean, Hash] fallbacks Enable fallbacks or specify fallbacks for this model backend
123
- # @option options_ [Boolean] dirty Enable dirty tracking for this model backend
122
+ # @option options_ [Boolean, Hash] fallbacks Enable fallbacks or specify
123
+ # fallbacks for this model backend
124
+ # @option options_ [Boolean] dirty Enable dirty tracking for this model
125
+ # backend
126
+ # @option options_ [Boolean] fallthrough_accessors Enable fallthrough
127
+ # locale accessors for this model backend
124
128
  # @raise [ArgumentError] if method is not reader, writer or accessor
125
129
  def initialize(method, *attributes_, **options_)
126
130
  raise ArgumentError, "method must be one of: reader, writer, accessor" unless %i[reader writer accessor].include?(method)
@@ -130,16 +134,16 @@ with other backends.
130
134
  @backend_name = options.delete(:backend) || Mobility.config.default_backend
131
135
  @backend_class = Class.new(get_backend_class(backend: @backend_name,
132
136
  model_class: model_class))
133
-
134
- options[:locale_accessors] ||= true if options[:dirty]
137
+ if (options[:dirty] && options[:fallthrough_accessors] != false)
138
+ options[:fallthrough_accessors] = true
139
+ end
135
140
 
136
141
  @backend_class.configure!(options) if @backend_class.respond_to?(:configure!)
137
142
 
138
- @backend_class.include Backend::Cache unless options[:cache] == false
139
- @backend_class.include Backend::Dirty.for(model_class) if options[:dirty]
140
- @backend_class.include Backend::Fallbacks if options[:fallbacks]
143
+ include_backend_modules(@backend_class, options)
144
+
141
145
  @accessor_locales = options[:locale_accessors]
142
- @accessor_locales = Mobility.config.default_accessor_locales.call if options[:locale_accessors] == true
146
+ @accessor_locales = Mobility.config.default_accessor_locales if @accessor_locales == true
143
147
 
144
148
  attributes.each do |attribute|
145
149
  define_backend(attribute)
@@ -162,6 +166,13 @@ with other backends.
162
166
  end
163
167
  end
164
168
 
169
+ def include_backend_modules(backend_class, options)
170
+ backend_class.include(Backend::Cache) unless options[:cache] == false
171
+ backend_class.include(Backend::Dirty.for(options[:model_class])) if options[:dirty]
172
+ backend_class.include(Backend::Fallbacks) if options[:fallbacks]
173
+ backend_class.include(FallthroughAccessors.new(attributes)) if options[:fallthrough_accessors]
174
+ end
175
+
165
176
  # Add this attributes module to shared {Mobility::Wrapper} and setup model
166
177
  # with backend setup block (see {Mobility::Backend::Setup#setup_model}).
167
178
  # @param model_class [Class] Class of model
@@ -23,12 +23,13 @@ value of the translated attribute if passed to it.
23
23
  module ActiveModel::Dirty
24
24
  # @!group Backend Accessors
25
25
  # @!macro backend_writer
26
+ # @param [Hash] options
26
27
  def write(locale, value, **options)
27
- locale_accessor = "#{attribute}_#{locale}"
28
+ locale_accessor = Mobility.normalize_locale_accessor(attribute, locale)
28
29
  if model.changed_attributes.has_key?(locale_accessor) && model.changed_attributes[locale_accessor] == value
29
30
  model.attributes_changed_by_setter.except!(locale_accessor)
30
31
  elsif read(locale, options.merge(fallbacks: false)) != value
31
- model.send(:attribute_will_change!, "#{attribute}_#{locale}")
32
+ model.send(:attribute_will_change!, locale_accessor)
32
33
  end
33
34
  super
34
35
  end
@@ -43,14 +44,14 @@ value of the translated attribute if passed to it.
43
44
  # methods for translated attributes onto model class.
44
45
  module ClassMethods
45
46
  # (see Mobility::Backend::Setup#setup_model)
46
- def setup_model(model_class, attributes, **)
47
+ def setup_model(model_class, attributes, **options)
47
48
  super
48
49
  model_class.class_eval do
49
50
  %w[changed? change was will_change! previously_changed? previous_change].each do |suffix|
50
51
  attributes.each do |attribute|
51
52
  class_eval <<-EOM, __FILE__, __LINE__ + 1
52
53
  def #{attribute}_#{suffix}
53
- attribute_#{suffix}("#{attribute}_#\{Mobility.locale\}")
54
+ attribute_#{suffix}(Mobility.normalize_locale_accessor("#{attribute}"))
54
55
  end
55
56
  EOM
56
57
  end
@@ -59,8 +60,8 @@ value of the translated attribute if passed to it.
59
60
 
60
61
  restore_methods = Module.new do
61
62
  attributes.each do |attribute|
62
- locale_accessor = "#{attribute}_#{Mobility.locale}"
63
63
  define_method "restore_#{attribute}!" do
64
+ locale_accessor = Mobility.normalize_locale_accessor(attribute)
64
65
  if attribute_changed?(locale_accessor)
65
66
  __send__("#{attribute}=", changed_attributes[locale_accessor])
66
67
  end
@@ -77,6 +78,8 @@ value of the translated attribute if passed to it.
77
78
  private :restore_attribute!
78
79
  end
79
80
  model_class.include restore_methods
81
+
82
+ model_class.include(FallthroughAccessors.new(*attributes))
80
83
  end
81
84
  end
82
85
  end
@@ -4,7 +4,7 @@ module Mobility
4
4
 
5
5
  Implements the {Mobility::Backend::Column} backend for ActiveRecord models.
6
6
 
7
- @note This backend disables the +accessor_locales+ option, which would
7
+ @note This backend disables the +locale_accessors+ option, which would
8
8
  otherwise interfere with column methods.
9
9
 
10
10
  @example
@@ -11,12 +11,12 @@ Internal class used by ActiveRecord backends that store values as a hash.
11
11
  # @!group Backend Accessors
12
12
  #
13
13
  # @!macro backend_reader
14
- def read(locale, **)
14
+ def read(locale, **_)
15
15
  translations[locale]
16
16
  end
17
17
 
18
18
  # @!macro backend_writer
19
- def write(locale, value, **)
19
+ def write(locale, value, **_)
20
20
  translations[locale] = value
21
21
  end
22
22
  # @!endgroup
@@ -18,7 +18,7 @@ Implements the {Mobility::Backend::Hstore} backend for ActiveRecord models.
18
18
 
19
19
  # @!group Backend Accessors
20
20
  # @!macro backend_writer
21
- def write(locale, value, **)
21
+ def write(locale, value, **_)
22
22
  translations[locale] = value && value.to_s
23
23
  end
24
24
  # @!endgroup
@@ -28,19 +28,19 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
28
28
 
29
29
  # @!macro backend_constructor
30
30
  # @option options [Symbol] association_name Name of association
31
- def initialize(model, attribute, **)
31
+ def initialize(model, attribute, **options)
32
32
  super
33
33
  @association_name = options[:association_name]
34
34
  end
35
35
 
36
36
  # @!group Backend Accessors
37
37
  # @!macro backend_reader
38
- def read(locale, **)
38
+ def read(locale, **_)
39
39
  translation_for(locale).value
40
40
  end
41
41
 
42
42
  # @!macro backend_reader
43
- def write(locale, value, **)
43
+ def write(locale, value, **_)
44
44
  translation_for(locale).tap { |t| t.value = value }.value
45
45
  end
46
46
  # @!endgroup
@@ -9,8 +9,7 @@ models. For details see backend-specific subclasses.
9
9
  =end
10
10
  class QueryMethods < Module
11
11
  # @param [Array<String>] attributes Translated attributes
12
- # @param [Hash] options Backend options
13
- def initialize(attributes, **)
12
+ def initialize(attributes, **_)
14
13
  @attributes = attributes
15
14
  @attributes_extractor = lambda do |opts|
16
15
  opts.is_a?(Hash) && (opts.keys.map(&:to_s) & attributes).presence
@@ -28,12 +28,12 @@ Implements {Mobility::Backend::Serialized} backend for ActiveRecord models.
28
28
  # @!group Backend Accessors
29
29
  #
30
30
  # @!macro backend_reader
31
- def read(locale, **)
31
+ def read(locale, **_)
32
32
  translations[locale]
33
33
  end
34
34
 
35
35
  # @!macro backend_reader
36
- def write(locale, value, **)
36
+ def write(locale, value, **_)
37
37
  translations[locale] = value
38
38
  end
39
39
  # @!endgroup
@@ -84,19 +84,19 @@ Implements the {Mobility::Backend::Table} backend for ActiveRecord models.
84
84
 
85
85
  # @!macro backend_constructor
86
86
  # @option options [Symbol] association_name Name of association
87
- def initialize(model, attribute, **)
87
+ def initialize(model, attribute, **options)
88
88
  super
89
89
  @association_name = options[:association_name]
90
90
  end
91
91
 
92
92
  # @!group Backend Accessors
93
93
  # @!macro backend_reader
94
- def read(locale, **)
94
+ def read(locale, **_)
95
95
  translation_for(locale).send(attribute)
96
96
  end
97
97
 
98
98
  # @!macro backend_reader
99
- def write(locale, value, **)
99
+ def write(locale, value, **_)
100
100
  translation_for(locale).tap { |t| t.send("#{attribute}=", value) }.send(attribute)
101
101
  end
102
102
  # @!endgroup
@@ -177,7 +177,7 @@ Implements the {Mobility::Backend::Table} backend for ActiveRecord models.
177
177
  private
178
178
 
179
179
  def translation_for(locale)
180
- translation = translations.find { |t| t.locale == locale.to_s }
180
+ translation = translations.find { |t| t.locale == locale.to_s.freeze }
181
181
  translation ||= translations.build(locale: locale)
182
182
  translation
183
183
  end
@@ -35,7 +35,7 @@ this).
35
35
  module Cache
36
36
  # @group Backend Accessors
37
37
  # @!macro backend_reader
38
- def read(locale, **)
38
+ def read(locale, **_)
39
39
  if write_to_cache? || cache.has_key?(locale)
40
40
  cache[locale]
41
41
  else
@@ -44,7 +44,7 @@ this).
44
44
  end
45
45
 
46
46
  # @!macro backend_writer
47
- def write(locale, value, **)
47
+ def write(locale, value, **_)
48
48
  cache[locale] = write_to_cache? ? value : super
49
49
  end
50
50
  # @!endgroup
@@ -56,8 +56,7 @@ this).
56
56
  module Setup
57
57
  # @param model_class Model class
58
58
  # @param [Array<String>] attributes Backend attributes
59
- # @param [Hash] options Backend options
60
- def setup_model(model_class, attributes, **)
59
+ def setup_model(model_class, attributes, **_)
61
60
  super
62
61
  model_class.include BackendResetter.for(model_class).new(attributes) { clear_cache }
63
62
  end
@@ -22,12 +22,12 @@ be ignored if set, since it would cause a conflict with column accessors.
22
22
  # @!group Backend Accessors
23
23
  #
24
24
  # @!macro backend_reader
25
- def read(locale, **)
25
+ def read(locale, **_)
26
26
  model.send(column(locale))
27
27
  end
28
28
 
29
29
  # @!macro backend_writer
30
- def write(locale, value, **)
30
+ def write(locale, value, **_)
31
31
  model.send("#{column(locale)}=", value)
32
32
  end
33
33
  # @!endgroup
@@ -44,7 +44,7 @@ be ignored if set, since it would cause a conflict with column accessors.
44
44
  # @param [Symbol] locale
45
45
  # @return [String]
46
46
  def self.column_name_for(attribute, locale = Mobility.locale)
47
- normalized_locale = locale.to_s.downcase.sub("-", "_")
47
+ normalized_locale = Mobility.normalize_locale(locale)
48
48
  "#{attribute}_#{normalized_locale}".to_sym
49
49
  end
50
50
  end
@@ -4,7 +4,7 @@ module Mobility
4
4
 
5
5
  Implements the {Mobility::Backend::Column} backend for Sequel models.
6
6
 
7
- @note This backend disables the +accessor_locales+ option, which would
7
+ @note This backend disables the +locale_accessors+ option, which would
8
8
  otherwise interfere with column methods.
9
9
  =end
10
10
  class Sequel::Column
@@ -12,12 +12,12 @@ Automatically includes dirty plugin in model class when enabled.
12
12
  # @!group Backend Accessors
13
13
  # @!macro backend_writer
14
14
  def write(locale, value, **options)
15
- locale_accessor = "#{attribute}_#{locale}".to_sym
15
+ locale_accessor = Mobility.normalize_locale_accessor(attribute, locale).to_sym
16
16
  if model.column_changes.has_key?(locale_accessor) && model.initial_values[locale_accessor] == value
17
17
  super
18
18
  [model.changed_columns, model.initial_values].each { |h| h.delete(locale_accessor) }
19
19
  elsif read(locale, options.merge(fallbacks: false)) != value
20
- model.will_change_column("#{attribute}_#{locale}".to_sym)
20
+ model.will_change_column(locale_accessor)
21
21
  super
22
22
  end
23
23
  end
@@ -32,7 +32,7 @@ Automatically includes dirty plugin in model class when enabled.
32
32
  # methods for translated attributes onto model class.
33
33
  module ClassMethods
34
34
  # (see Mobility::Backend::Setup#setup_model)
35
- def setup_model(model_class, attributes, **)
35
+ def setup_model(model_class, attributes, **options)
36
36
  super
37
37
  model_class.plugin :dirty
38
38
  model_class.class_eval do
@@ -40,7 +40,7 @@ Automatically includes dirty plugin in model class when enabled.
40
40
  %w[initial_value column_change column_changed? reset_column].each do |method_name|
41
41
  define_method method_name do |column|
42
42
  if attributes.map(&:to_sym).include?(column)
43
- super("#{column}_#{Mobility.locale}".to_sym)
43
+ super(Mobility.normalize_locale_accessor(column).to_sym)
44
44
  else
45
45
  super(column)
46
46
  end
@@ -49,6 +49,8 @@ Automatically includes dirty plugin in model class when enabled.
49
49
  end
50
50
  include mod
51
51
  end
52
+
53
+ model_class.include(FallthroughAccessors.new(*attributes))
52
54
  end
53
55
  end
54
56
  end
@@ -9,12 +9,12 @@ Internal class used by Sequel backends that store values as a hash.
9
9
  include Backend
10
10
 
11
11
  # @!macro backend_reader
12
- def read(locale, **)
12
+ def read(locale, **_)
13
13
  translations[locale.to_s]
14
14
  end
15
15
 
16
16
  # @!macro backend_writer
17
- def write(locale, value, **)
17
+ def write(locale, value, **_)
18
18
  translations[locale.to_s] = value
19
19
  end
20
20
 
@@ -18,7 +18,7 @@ Implements the {Mobility::Backend::Hstore} backend for Sequel models.
18
18
 
19
19
  # @!group Backend Accessors
20
20
  # @!macro backend_writer
21
- def write(locale, value, **)
21
+ def write(locale, value, **_)
22
22
  translations[locale.to_s] = value && value.to_s
23
23
  end
24
24
  # @!endgroup
@@ -31,12 +31,12 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
31
31
 
32
32
  # @!group Backend Accessors
33
33
  # @!macro backend_reader
34
- def read(locale, **)
34
+ def read(locale, **_)
35
35
  translation_for(locale).value
36
36
  end
37
37
 
38
38
  # @!macro backend_writer
39
- def write(locale, value, **)
39
+ def write(locale, value, **_)
40
40
  translation_for(locale).tap { |t| t.value = value }.value
41
41
  end
42
42
  # @!endgroup
@@ -9,8 +9,7 @@ models. For details see backend-specific subclasses.
9
9
  =end
10
10
  class QueryMethods < Module
11
11
  # @param [Array<String>] attributes Translated attributes
12
- # @param [Hash] options Backend options
13
- def initialize(attributes, **)
12
+ def initialize(attributes, **_)
14
13
  @attributes = attributes.map! &:to_sym
15
14
  @attributes_extractor = lambda do |cond|
16
15
  cond.is_a?(Hash) && (cond.keys & attributes).presence
@@ -35,12 +35,12 @@ Sequel serialization plugin.
35
35
  # @!group Backend Accessors
36
36
  #
37
37
  # @!macro backend_reader
38
- def read(locale, **)
38
+ def read(locale, **_)
39
39
  translations[locale]
40
40
  end
41
41
 
42
42
  # @!macro backend_reader
43
- def write(locale, value, **)
43
+ def write(locale, value, **_)
44
44
  translations[locale] = value
45
45
  end
46
46
  # @!endgroup
@@ -15,19 +15,19 @@ Implements the {Mobility::Backend::Table} backend for Sequel models.
15
15
 
16
16
  # @!macro backend_constructor
17
17
  # @option options [Symbol] association_name Name of association
18
- def initialize(model, attribute, **)
18
+ def initialize(model, attribute, **options)
19
19
  super
20
20
  @association_name = options[:association_name]
21
21
  end
22
22
 
23
23
  # @!group Backend Accessors
24
24
  # @!macro backend_reader
25
- def read(locale, **)
25
+ def read(locale, **_)
26
26
  translation_for(locale).send(attribute)
27
27
  end
28
28
 
29
29
  # @!macro backend_reader
30
- def write(locale, value, **)
30
+ def write(locale, value, **_)
31
31
  translation_for(locale).tap { |t| t.send("#{attribute}=", value) }.send(attribute)
32
32
  end
33
33
 
@@ -87,13 +87,11 @@ On top of this, a backend will normally:
87
87
 
88
88
  # @!macro [new] backend_reader
89
89
  # @param [Symbol] locale Locale to read
90
- # @param [Hash] options
91
90
  # @return [Object] Value of translation
92
91
  #
93
92
  # @!macro [new] backend_writer
94
93
  # @param [Symbol] locale Locale to write
95
94
  # @param [Object] value Value to write
96
- # @param [Hash] options
97
95
  # @return [Object] Updated value
98
96
 
99
97
  # Extend included class with +setup+ method
@@ -17,10 +17,17 @@ Stores shared Mobility configuration referenced by all backends.
17
17
  # @return [Symbol,Class]
18
18
  attr_accessor :default_backend
19
19
 
20
- # Proc returning set of default accessor locles to use (defaults to proc
21
- # returning +I18n.available_locales+)
20
+ # Returns set of default accessor locles to use (defaults to
21
+ # +I18n.available_locales+)
22
22
  # @return [Array<Symbol>]
23
- attr_accessor :default_accessor_locales
23
+ def default_accessor_locales
24
+ if @default_accessor_locales.is_a?(Proc)
25
+ @default_accessor_locales.call
26
+ else
27
+ @default_accessor_locales
28
+ end
29
+ end
30
+ attr_writer :default_accessor_locales
24
31
 
25
32
  def initialize
26
33
  @accessor_method = :translates
@@ -0,0 +1,57 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Mobility
4
+ =begin
5
+
6
+ Defines +method_missing+ and +respond_to_missing?+ methods for a set of
7
+ attributes such that a method call using a locale accessor, like:
8
+
9
+ article.title_pt_br
10
+
11
+ will return the value of +article.title+ with the locale set to +pt-BR+ around
12
+ the method call. The class is called "FallthroughAccessors" because when
13
+ included in a model class, locale-specific methods will be available even if
14
+ not explicitly defined with the +locale_accessors+ option.
15
+
16
+ This is a less efficient (but more open-ended) implementation of locale
17
+ accessors, for use in cases where the locales to be used are not known when the
18
+ model class is generated.
19
+
20
+ @example Using fallthrough locales on a plain old ruby class
21
+ class Post
22
+ def title
23
+ "title in #{Mobility.locale}"
24
+ end
25
+ include Mobility::FallthroughAccessors.new("title")
26
+ end
27
+
28
+ Mobility.locale = :en
29
+ post = Post.new
30
+ post.title
31
+ #=> "title in en"
32
+ post.title_fr
33
+ #=> "title in fr"
34
+
35
+ =end
36
+ class FallthroughAccessors < Module
37
+ # @param [Array<String>] Array of attributes
38
+ def initialize(*attributes)
39
+ method_name_regex = /\A(#{attributes.join('|'.freeze)})_([a-z]{2}(_[a-z]{2})?)(=?)\z/.freeze
40
+
41
+ define_method :method_missing do |method_name, *arguments, &block|
42
+ if method_name =~ method_name_regex
43
+ attribute = $1.to_sym
44
+ locale, suffix = $2.split('_'.freeze)
45
+ locale = "#{locale}-#{suffix.upcase}".freeze if suffix
46
+ Mobility.with_locale(locale) { public_send("#{attribute}#{$4}".freeze, *arguments) }
47
+ else
48
+ super(method_name, *arguments, &block)
49
+ end
50
+ end
51
+
52
+ define_method :respond_to_missing? do |method_name, include_private = false|
53
+ (method_name =~ method_name_regex) || super(method_name, include_private)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -14,7 +14,7 @@ is called.
14
14
  define_method :mobility_set do |attribute, value, locale: Mobility.locale|
15
15
  if attributes.include?(attribute)
16
16
  column = attribute.to_sym
17
- column_with_locale = :"#{attribute}_#{locale}"
17
+ column_with_locale = :"#{attribute}_#{Mobility.normalize_locale(locale)}"
18
18
  if mobility_get(attribute) != value
19
19
  @changed_columns << column_with_locale if !changed_columns.include?(column_with_locale)
20
20
  @changed_columns << column if !changed_columns.include?(column)
@@ -1,3 +1,3 @@
1
1
  module Mobility
2
- VERSION = "0.1.5"
2
+ VERSION = "0.1.6"
3
3
  end
data/lib/mobility.rb CHANGED
@@ -32,13 +32,14 @@ in backends to define gem-dependent behavior.
32
32
 
33
33
  =end
34
34
  module Mobility
35
- autoload :Attributes, "mobility/attributes"
36
- autoload :Backend, "mobility/backend"
37
- autoload :BackendResetter, "mobility/backend_resetter"
38
- autoload :Configuration, "mobility/configuration"
39
- autoload :InstanceMethods, "mobility/instance_methods"
40
- autoload :Translates, "mobility/translates"
41
- autoload :Wrapper, "mobility/wrapper"
35
+ autoload :Attributes, "mobility/attributes"
36
+ autoload :Backend, "mobility/backend"
37
+ autoload :BackendResetter, "mobility/backend_resetter"
38
+ autoload :Configuration, "mobility/configuration"
39
+ autoload :FallthroughAccessors, "mobility/fallthrough_accessors"
40
+ autoload :InstanceMethods, "mobility/instance_methods"
41
+ autoload :Translates, "mobility/translates"
42
+ autoload :Wrapper, "mobility/wrapper"
42
43
 
43
44
  require "mobility/orm"
44
45
 
@@ -122,7 +123,8 @@ module Mobility
122
123
 
123
124
  # Sets Mobility locale
124
125
  # @param [Symbol] locale Locale to set
125
- # @raise [InvalidLocale] if locale is nil or not in +I18n.available_locales
126
+ # @raise [InvalidLocale] if locale is nil or not in
127
+ # +I18n.available_locales+ (if +I18n.enforce_available_locales+ is +true+)
126
128
  # @return [Symbol] Locale
127
129
  def locale=(locale)
128
130
  set_locale(locale)
@@ -185,8 +187,22 @@ module Mobility
185
187
  # #=> "ja"
186
188
  # Mobility.normalize_locale("pt-BR")
187
189
  # #=> "pt_br"
188
- def normalize_locale(locale)
189
- "#{locale.to_s.downcase.sub("-", "_")}"
190
+ def normalize_locale(locale = Mobility.locale)
191
+ "#{locale.to_s.downcase.sub("-", "_")}".freeze
192
+ end
193
+ alias_method :normalized_locale, :normalize_locale
194
+
195
+ # Return normalized locale accessor name
196
+ # @param [String,Symbol] attribute
197
+ # @param [String,Symbol] locale
198
+ # @return [String] Normalized locale accessor name
199
+ # @example
200
+ # Mobility.normalize_locale_accessor(:foo, :ja)
201
+ # #=> "foo_ja"
202
+ # Mobility.normalize_locale_accessor(:bar, "pt-BR")
203
+ # #=> "bar_pt_br"
204
+ def normalize_locale_accessor(attribute, locale = Mobility.locale)
205
+ "#{attribute}_#{normalize_locale(locale)}".freeze
190
206
  end
191
207
 
192
208
  protected
@@ -197,7 +213,9 @@ module Mobility
197
213
 
198
214
  def set_locale(locale)
199
215
  locale = locale.to_sym if locale
200
- raise Mobility::InvalidLocale.new(locale) unless I18n.available_locales.include?(locale) || locale.nil?
216
+ if I18n.enforce_available_locales
217
+ raise Mobility::InvalidLocale.new(locale) unless (I18n.available_locales.include?(locale) || locale.nil?)
218
+ end
201
219
  storage[:mobility_locale] = locale
202
220
  end
203
221
  end
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.5
4
+ version: 0.1.6
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-12 00:00:00.000000000 Z
11
+ date: 2017-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: request_store
@@ -140,8 +140,6 @@ files:
140
140
  - lib/generators/mobility/install_generator.rb
141
141
  - lib/generators/mobility/templates/create_string_translations.rb
142
142
  - lib/generators/mobility/templates/create_text_translations.rb
143
- - lib/generators/mobility/templates/translations.rb
144
- - lib/generators/mobility/translations_generator.rb
145
143
  - lib/mobility.rb
146
144
  - lib/mobility/active_model.rb
147
145
  - lib/mobility/active_model/attribute_methods.rb
@@ -176,7 +174,6 @@ files:
176
174
  - lib/mobility/backend/dirty.rb
177
175
  - lib/mobility/backend/fallbacks.rb
178
176
  - lib/mobility/backend/hstore.rb
179
- - lib/mobility/backend/i18n.rb
180
177
  - lib/mobility/backend/jsonb.rb
181
178
  - lib/mobility/backend/key_value.rb
182
179
  - lib/mobility/backend/null.rb
@@ -203,6 +200,7 @@ files:
203
200
  - lib/mobility/configuration.rb
204
201
  - lib/mobility/core_ext/object.rb
205
202
  - lib/mobility/core_ext/string.rb
203
+ - lib/mobility/fallthrough_accessors.rb
206
204
  - lib/mobility/instance_methods.rb
207
205
  - lib/mobility/orm.rb
208
206
  - lib/mobility/sequel.rb
@@ -1,46 +0,0 @@
1
- class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
- <%- if migration_action == 'add' -%>
3
- def change
4
- <% attributes.each do |attribute| -%>
5
- <%- if attribute.reference? -%>
6
- add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
7
- <%- elsif attribute.token? -%>
8
- add_column :<%= table_name %>, :<%= attribute.name %>, :string<%= attribute.inject_options %>
9
- add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>, unique: true
10
- <%- else -%>
11
- add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
12
- <%- if attribute.has_index? -%>
13
- add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
14
- <%- end -%>
15
- <%- end -%>
16
- <%- end -%>
17
- end
18
- <%- elsif migration_action == 'join' -%>
19
- def change
20
- create_join_table :<%= join_tables.first %>, :<%= join_tables.second %> do |t|
21
- <%- attributes.each do |attribute| -%>
22
- <%- if attribute.reference? -%>
23
- t.references :<%= attribute.name %><%= attribute.inject_options %>
24
- <%- else -%>
25
- <%= '# ' unless attribute.has_index? -%>t.index <%= attribute.index_name %><%= attribute.inject_index_options %>
26
- <%- end -%>
27
- <%- end -%>
28
- end
29
- end
30
- <%- else -%>
31
- def change
32
- <% attributes.each do |attribute| -%>
33
- <%- if migration_action -%>
34
- <%- if attribute.reference? -%>
35
- remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %>
36
- <%- else -%>
37
- <%- if attribute.has_index? -%>
38
- remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %>
39
- <%- end -%>
40
- remove_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %>
41
- <%- end -%>
42
- <%- end -%>
43
- <%- end -%>
44
- end
45
- <%- end -%>
46
- end
@@ -1,20 +0,0 @@
1
- require "rails/generators"
2
- require "rails/generators/active_record/migration/migration_generator"
3
-
4
- module Mobility
5
- class TranslationsGenerator < ::Rails::Generators::NamedBase
6
- include Rails::Generators::Migration
7
-
8
- argument :attributes, type: :array, default: [], banner: "field[:type] field[:type]"
9
- source_root File.expand_path("../templates", __FILE__)
10
- class_option(
11
- :backend,
12
- type: :string,
13
- desc: "Backend to use for translations (defaults to Mobility.default_backend)"
14
- )
15
-
16
- def self.next_migration_number(dirname)
17
- ::ActiveRecord::Generators::Base.next_migration_number(dirname)
18
- end
19
- end
20
- end
@@ -1,28 +0,0 @@
1
- # module Mobility
2
- # module Backend
3
- # class I18n
4
- # include Backend
5
-
6
- # attr_reader :i18n_key
7
- # attr_reader :mapping
8
-
9
- # def initialize(model, attribute, **)
10
- # super
11
- # @key_map = options[:key_map]
12
- # end
13
-
14
- # def read(locale, **)
15
- # I18n.t(i18n_key)
16
- # end
17
-
18
- # def self.configure!(options)
19
- # raise ArgumentError, "missing key_map" unless options[:key_map].present?
20
- # end
21
-
22
- # private
23
-
24
- # def i18n_key
25
- # key_map.call(model, attribute))
26
- # end
27
- # end
28
- # end