mobility 0.1.5 → 0.1.6

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