mobility 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +21 -0
  5. data/Gemfile +11 -9
  6. data/Gemfile.lock +9 -6
  7. data/README.md +50 -16
  8. data/lib/mobility.rb +18 -4
  9. data/lib/mobility/active_record.rb +14 -7
  10. data/lib/mobility/active_record/uniqueness_validator.rb +17 -3
  11. data/lib/mobility/attributes.rb +39 -16
  12. data/lib/mobility/backends/active_record/column/query_methods.rb +12 -11
  13. data/lib/mobility/backends/active_record/container.rb +116 -0
  14. data/lib/mobility/backends/active_record/container/query_methods.rb +31 -0
  15. data/lib/mobility/backends/active_record/hstore/query_methods.rb +7 -54
  16. data/lib/mobility/backends/active_record/jsonb/query_methods.rb +5 -55
  17. data/lib/mobility/backends/active_record/key_value.rb +13 -9
  18. data/lib/mobility/backends/active_record/key_value/query_methods.rb +6 -6
  19. data/lib/mobility/backends/active_record/pg_query_methods.rb +115 -0
  20. data/lib/mobility/backends/active_record/query_methods.rb +4 -3
  21. data/lib/mobility/backends/active_record/serialized/query_methods.rb +6 -5
  22. data/lib/mobility/backends/active_record/table.rb +18 -3
  23. data/lib/mobility/backends/active_record/table/query_methods.rb +25 -14
  24. data/lib/mobility/backends/container.rb +25 -0
  25. data/lib/mobility/backends/hash_valued.rb +2 -2
  26. data/lib/mobility/backends/null.rb +2 -2
  27. data/lib/mobility/backends/sequel/column.rb +2 -2
  28. data/lib/mobility/backends/sequel/column/query_methods.rb +2 -2
  29. data/lib/mobility/backends/sequel/container.rb +99 -0
  30. data/lib/mobility/backends/sequel/container/query_methods.rb +41 -0
  31. data/lib/mobility/backends/sequel/hstore/query_methods.rb +17 -3
  32. data/lib/mobility/backends/sequel/jsonb/query_methods.rb +17 -3
  33. data/lib/mobility/backends/sequel/key_value.rb +2 -1
  34. data/lib/mobility/backends/sequel/key_value/query_methods.rb +2 -2
  35. data/lib/mobility/backends/sequel/pg_hash.rb +4 -7
  36. data/lib/mobility/backends/sequel/pg_query_methods.rb +85 -0
  37. data/lib/mobility/backends/sequel/query_methods.rb +4 -3
  38. data/lib/mobility/backends/sequel/serialized/query_methods.rb +4 -3
  39. data/lib/mobility/backends/sequel/table.rb +1 -1
  40. data/lib/mobility/backends/sequel/table/query_methods.rb +2 -2
  41. data/lib/mobility/backends/serialized.rb +5 -7
  42. data/lib/mobility/configuration.rb +34 -4
  43. data/lib/mobility/plugins/active_record/dirty.rb +1 -1
  44. data/lib/mobility/plugins/fallbacks.rb +20 -6
  45. data/lib/mobility/sequel/column_changes.rb +2 -5
  46. data/lib/mobility/sequel/hash_initializer.rb +19 -0
  47. data/lib/mobility/util.rb +0 -2
  48. data/lib/mobility/version.rb +1 -1
  49. metadata +11 -4
  50. metadata.gz.sig +0 -0
  51. data/lib/mobility/backends/sequel/postgres_query_methods.rb +0 -41
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc2df8397e85a1748dabf599e88d5e351a67cb8e
4
- data.tar.gz: 53329705a4e2f67cd9934cb3c791e43e7f32b5f2
3
+ metadata.gz: aca3c693e765d0214134a9aba41659f40f74f3a0
4
+ data.tar.gz: 4f3fd42701ae4dd0bfa0c6ee990a296ec4364d7c
5
5
  SHA512:
6
- metadata.gz: 0df70deea72e9a4698c2946da51a069a8cd46b42c222bbe7a67b9549c407fa02b994c76e5ad8a02c4527fedd6035c670a9766dda3458350a7e02883daf979eb1
7
- data.tar.gz: 8e67332a56f03d5ae6aa261ebdd9daa171105f85e254a0c10702af072a24314458c196967ef0edfa125af096d9dd03e9f8154d677cc2bb2701dd98792450dcf8
6
+ metadata.gz: 97ffa08e1c6cea2d3301bd642c1ce5c7ea86aae11d59402471afc236cdcc53b6daea6ac9bcfaf34b6cc7e509f09aed205e8b1cba65e1614adb2fd75f0107fda8
7
+ data.tar.gz: dc1581bded2b253d645172ad28b1e0f1cede386633d1a4d343603869da6561a165b408b7b6ca7722eca5ada6c1c29052b7c7c3f2f9f24f122b25db0fdc9001f2
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Mobility Changelog
2
2
 
3
+ ## 0.4
4
+
5
+ ### 0.4.0 (January 24, 2018)
6
+ * Add new jsonb Container backend
7
+ ([#157](https://github.com/shioyama/mobility/pull/157))
8
+ * Define attributes accessors with eval
9
+ ([#152](https://github.com/shioyama/mobility/pull/152))
10
+ * Rename `default_fallbacks` to `new_fallbacks` / `fallbacks_generator=`
11
+ ([#148](https://github.com/shioyama/mobility/pull/148))
12
+ * Warn user if `case_sensitive` option is passed to ActiveRecord uniqueness
13
+ validator ([#146](https://github.com/shioyama/mobility/pull/146))
14
+ * Handle array of values to translated attribute query
15
+ ([#128](https://github.com/shioyama/mobility/pull/128))
16
+ * Use module builder instance to define shared methods in closure
17
+ ([#130](https://github.com/shioyama/mobility/pull/130))
18
+ * Query on translated json value with Sequel ORM
19
+ ([#155](https://github.com/shioyama/mobility/pull/155))
20
+ * Refactor pg query methods ([#129](https://github.com/shioyama/mobility/pull/129))
21
+ * Reduce object allocations
22
+ ([#156](https://github.com/shioyama/mobility/pull/156))
23
+
3
24
  ## 0.3
4
25
 
5
26
  ### 0.3.6 (December 25, 2017)
data/Gemfile CHANGED
@@ -9,20 +9,18 @@ group :development, :test do
9
9
  gem 'activerecord', '>= 5.0', '< 5.1'
10
10
  elsif ENV['RAILS_VERSION'] == '4.2'
11
11
  gem 'activerecord', '>= 4.2.6', '< 5.0'
12
- elsif ENV['RAILS_VERSION'] == '5.2'
12
+ elsif ENV['RAILS_VERSION'] == '5.1'
13
+ gem 'activerecord', '>= 5.1', '< 5.2'
14
+ else
13
15
  gem 'activerecord', '>= 5.2.0.beta1', '< 5.3'
14
16
  gem 'railties', '>= 5.2.0.beta1', '< 5.3'
15
- else
16
- gem 'activerecord', '>= 5.1', '< 5.2'
17
17
  end
18
18
  gem "generator_spec", '~> 0.9.4'
19
19
  elsif ENV['ORM'] == 'sequel'
20
- if ENV['SEQUEL_VERSION'] == '4.41'
21
- gem 'sequel', '>= 4.41.0', '< 4.46.0'
22
- elsif ENV['SEQUEL_VERSION'] == '5.0'
23
- gem 'sequel', '>= 5.0.0', '< 6.0.0'
24
- else
20
+ if ENV['SEQUEL_VERSION'] == '4'
25
21
  gem 'sequel', '>= 4.46.0', '< 5.0'
22
+ else
23
+ gem 'sequel', '>= 5.0.0', '< 6.0.0'
26
24
  end
27
25
  end
28
26
 
@@ -33,6 +31,10 @@ group :development, :test do
33
31
  gem 'pry-byebug'
34
32
  gem 'sqlite3'
35
33
  gem 'mysql2', '~> 0.4.9'
36
- gem 'pg'
34
+ gem 'pg', '< 1.0'
37
35
  end
38
36
  end
37
+
38
+ group :benchmark do
39
+ gem "benchmark-ips"
40
+ end
data/Gemfile.lock CHANGED
@@ -33,6 +33,7 @@ GEM
33
33
  minitest (~> 5.1)
34
34
  tzinfo (~> 1.1)
35
35
  arel (9.0.0)
36
+ benchmark-ips (2.7.2)
36
37
  builder (3.2.3)
37
38
  byebug (9.1.0)
38
39
  coderay (1.1.2)
@@ -46,10 +47,10 @@ GEM
46
47
  generator_spec (0.9.4)
47
48
  activesupport (>= 3.0.0)
48
49
  railties (>= 3.0.0)
49
- guard (2.14.1)
50
+ guard (2.14.2)
50
51
  formatador (>= 0.2.4)
51
52
  listen (>= 2.7, < 4.0)
52
- lumberjack (~> 1.0)
53
+ lumberjack (>= 1.0.12, < 2.0)
53
54
  nenv (~> 0.1)
54
55
  notiffany (~> 0.0)
55
56
  pry (>= 0.9.12)
@@ -72,7 +73,7 @@ GEM
72
73
  lumberjack (1.0.12)
73
74
  method_source (0.9.0)
74
75
  mini_portile2 (2.3.0)
75
- minitest (5.10.3)
76
+ minitest (5.11.1)
76
77
  mysql2 (0.4.10)
77
78
  nenv (0.3.0)
78
79
  nokogiri (1.8.1)
@@ -105,12 +106,13 @@ GEM
105
106
  rb-fsevent (0.10.2)
106
107
  rb-inotify (0.9.10)
107
108
  ffi (>= 0.5.0, < 2)
108
- request_store (1.3.2)
109
+ request_store (1.4.0)
110
+ rack (>= 1.4)
109
111
  rspec (3.7.0)
110
112
  rspec-core (~> 3.7.0)
111
113
  rspec-expectations (~> 3.7.0)
112
114
  rspec-mocks (~> 3.7.0)
113
- rspec-core (3.7.0)
115
+ rspec-core (3.7.1)
114
116
  rspec-support (~> 3.7.0)
115
117
  rspec-expectations (3.7.0)
116
118
  diff-lcs (>= 1.2.0, < 2.0)
@@ -133,13 +135,14 @@ PLATFORMS
133
135
 
134
136
  DEPENDENCIES
135
137
  activerecord (>= 5.2.0.beta1, < 5.3)
138
+ benchmark-ips
136
139
  bundler (~> 1.12)
137
140
  database_cleaner (~> 1.5, >= 1.5.3)
138
141
  generator_spec (~> 0.9.4)
139
142
  guard-rspec
140
143
  mobility!
141
144
  mysql2 (~> 0.4.9)
142
- pg
145
+ pg (< 1.0)
143
146
  pry-byebug
144
147
  railties (>= 5.2.0.beta1, < 5.3)
145
148
  rake (~> 12, >= 12.2.1)
data/README.md CHANGED
@@ -29,7 +29,7 @@ columns](http://dejimata.com/2017/3/3/translating-with-mobility#strategy-1) and
29
29
  [model translation
30
30
  tables](http://dejimata.com/2017/3/3/translating-with-mobility#strategy-2), as
31
31
  well as database-specific storage solutions such as
32
- [jsonb](https://www.postgresql.org/docs/current/static/datatype-json.html ) and
32
+ [jsonb](https://www.postgresql.org/docs/current/static/datatype-json.html) and
33
33
  [Hstore](https://www.postgresql.org/docs/current/static/hstore.html) (for
34
34
  PostgreSQL).
35
35
 
@@ -54,7 +54,7 @@ Installation
54
54
  Add this line to your application's Gemfile:
55
55
 
56
56
  ```ruby
57
- gem 'mobility', '~> 0.3.6'
57
+ gem 'mobility', '~> 0.4.0'
58
58
  ```
59
59
 
60
60
  Mobility is cryptographically signed. To be sure the gem you install hasn't
@@ -103,7 +103,23 @@ end
103
103
  ```
104
104
 
105
105
  To use a different default backend, set `default_backend` to another value (see
106
- possibilities [below](#backends)). Other configuration options are
106
+ possibilities [below](#backends)).
107
+
108
+ You will likely also want to set default values for the various translation
109
+ options described below. You can set these defaults by assigning values to keys
110
+ on the `config.default_options` hash, like this (to set the default value for
111
+ the `dirty` option to `true`):
112
+
113
+ ```diff
114
+ Mobility.configure do |config|
115
+ config.default_backend = :key_value
116
+ config.accessor_method = :translates
117
+ config.query_method = :i18n
118
+ + config.default_options[:dirty] = true
119
+ end
120
+ ```
121
+
122
+ Other configuration options are
107
123
  described in the [API
108
124
  docs](http://www.rubydoc.info/gems/mobility/Mobility/Configuration).
109
125
 
@@ -407,7 +423,7 @@ end
407
423
 
408
424
  Internally, Mobility assigns the fallbacks hash to an instance of
409
425
  `I18n::Locale::Fallbacks.new` (this can be customized by setting the
410
- `default_fallbacks` configuration option, see the [API documentation on
426
+ `fallbacks_generator` configuration option, see the [API documentation on
411
427
  configuration](http://www.rubydoc.info/gems/mobility/Mobility/Configuration)).
412
428
 
413
429
  By setting fallbacks for German and French to Japanese, values will fall
@@ -484,7 +500,7 @@ Mobility.with_locale(:de) { word.meaning }
484
500
  ```
485
501
 
486
502
  For more details, see the [API documentation on
487
- fallbacks](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Fallbacks)
503
+ fallbacks](http://www.rubydoc.info/gems/mobility/Mobility/Plugins/Fallbacks)
488
504
  and [this article on I18n
489
505
  fallbacks](https://github.com/svenfuchs/i18n/wiki/Fallbacks).
490
506
 
@@ -518,8 +534,10 @@ word.name(default: 'bar')
518
534
  #=> 'bar'
519
535
  ```
520
536
 
521
- The default can also be a `Proc`, which will be passed the model and attribute
522
- name as keyword arguments. See the [API docs][docs] for details.
537
+ The default can also be a `Proc`, which will be called with the context as the
538
+ model itself, and passed optional arguments (attribute, locale and options
539
+ passed to accessor) which can be used to customize behaviour. See the [API
540
+ docs][docs] for details.
523
541
 
524
542
  ### <a name="dirty"></a>Dirty Tracking
525
543
 
@@ -545,6 +563,9 @@ class Post < ApplicationRecord
545
563
  end
546
564
  ```
547
565
 
566
+ (If you want to enable dirty tracking on all models, set the
567
+ `config.default_options[:dirty]` option in your Mobility configuration.)
568
+
548
569
  Let's assume we start with a post with a title in English and Japanese:
549
570
 
550
571
  ```ruby
@@ -602,8 +623,16 @@ Notice that Mobility uses locale suffixes to indicate which locale has changed;
602
623
  dirty tracking is implemented this way to ensure that it is clear what
603
624
  has changed in which locale, avoiding any possible ambiguity.
604
625
 
605
- For more details, see the [API documentation on dirty
606
- tracking](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Dirty).
626
+ For performance reasons, it is highly recommended that when using the Dirty
627
+ plugin, you also enable [locale accessors](#getset) for all locales which will
628
+ be used, so that methods like `title_en` above are defined; otherwise they will
629
+ be caught by `method_missing` (using fallthrough accessors), which is much slower.
630
+ The easiest way to do this is to set `config.default_options[:locale_accessors]
631
+ = true` in your Mobility config, and make sure that `I18n.available_locales`
632
+ includes all locales you use in production.
633
+
634
+ For more details on dirty tracking, see the [API
635
+ documentation](http://www.rubydoc.info/gems/mobility/Mobility/Plugins/Dirty).
607
636
 
608
637
  ### Cache
609
638
 
@@ -743,7 +772,7 @@ This will generate the `post_translations` table with columns `title` and
743
772
  the [Table
744
773
  Backend](https://github.com/shioyama/mobility/wiki/Table-Backend) page of the
745
774
  wiki and API documentation on the [`Mobility::Backend::Table`
746
- class](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Table).
775
+ class](http://www.rubydoc.info/gems/mobility/Mobility/Backends/Table).
747
776
 
748
777
  ### Column Backend (like Traco)
749
778
 
@@ -761,7 +790,7 @@ rails generate mobility:translations post title:string content:text
761
790
  For more details, see the [Column
762
791
  Backend](https://github.com/shioyama/mobility/wiki/Column-Backend) page of the
763
792
  wiki and API documentation on the [`Mobility::Backend::Column`
764
- class](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Column).
793
+ class](http://www.rubydoc.info/gems/mobility/Mobility/Backends/Column).
765
794
 
766
795
  ### PostgreSQL-specific Backends
767
796
 
@@ -775,12 +804,17 @@ or
775
804
  extensions with `DB.extension :pg_json` or `DB.extension :pg_hstore` (where
776
805
  `DB` is your database instance).
777
806
 
778
- Other details are covered in the [Postgres
779
- Backend](https://github.com/shioyama/mobility/wiki/Postgres-Backends) page of
780
- the wiki and in the API documentation
781
- ([`Mobility::Backend::Jsonb`](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Jsonb)
807
+ Another option is to store all your translations on a single jsonb column (one
808
+ per model). This is called the "container" backend.
809
+
810
+ For details on these backends, see the [Postgres
811
+ Backend](https://github.com/shioyama/mobility/wiki/Postgres-Backends-%28Column-Attribute%29)
812
+ and [Container
813
+ Backend](https://github.com/shioyama/mobility/wiki/Container-Backend)
814
+ pages of the wiki and in the API documentation
815
+ ([`Mobility::Backend::Jsonb`](http://www.rubydoc.info/gems/mobility/Mobility/Backends/Jsonb)
782
816
  and
783
- [`Mobility::Backend::Hstore`](http://www.rubydoc.info/gems/mobility/Mobility/Backend/Hstore)).
817
+ [`Mobility::Backend::Hstore`](http://www.rubydoc.info/gems/mobility/Mobility/Backends/Hstore)).
784
818
 
785
819
  Development
786
820
  -----------
data/lib/mobility.rb CHANGED
@@ -149,8 +149,8 @@ module Mobility
149
149
  # (see Mobility::Configuration#query_method)
150
150
  # @!method query_method
151
151
 
152
- # (see Mobility::Configuration#default_fallbacks)
153
- # @!method default_fallbacks
152
+ # (see Mobility::Configuration#new_fallbacks)
153
+ # @!method new_fallbacks
154
154
 
155
155
  # (see Mobility::Configuration#default_backend)
156
156
  # @!method default_backend
@@ -169,10 +169,15 @@ module Mobility
169
169
  end
170
170
  end
171
171
 
172
- define_method :default_fallbacks do |*args|
172
+ # TODO: Remove in v1.0
173
+ def default_fallbacks(*args)
173
174
  config.public_send(:default_fallbacks, *args)
174
175
  end
175
176
 
177
+ def new_fallbacks(*args)
178
+ config.public_send(:new_fallbacks, *args)
179
+ end
180
+
176
181
  # Configure Mobility
177
182
  # @yield [Mobility::Configuration] Mobility configuration
178
183
  def configure
@@ -210,8 +215,16 @@ module Mobility
210
215
  # @param [String,Symbol] locale
211
216
  # @raise [InvalidLocale] if locale is present but not available
212
217
  def enforce_available_locales!(locale)
218
+ # TODO: Remove conditional in v1.0
213
219
  if I18n.enforce_available_locales
214
220
  raise Mobility::InvalidLocale.new(locale) unless (I18n.locale_available?(locale) || locale.nil?)
221
+ else
222
+ warn <<-EOL
223
+ WARNING: You called Mobility.enforce_available_locales! in a situation where
224
+ I18n.enforce_available_locales is false. In the past, Mobility would do nothing
225
+ in this case, but as of the next major release Mobility will ignore the I18n
226
+ setting and enforce available locales whenever this method is called.
227
+ EOL
215
228
  end
216
229
  end
217
230
 
@@ -223,7 +236,7 @@ module Mobility
223
236
 
224
237
  def set_locale(locale)
225
238
  locale = locale.to_sym if locale
226
- enforce_available_locales!(locale)
239
+ enforce_available_locales!(locale) if I18n.enforce_available_locales
227
240
  storage[:mobility_locale] = locale
228
241
  end
229
242
  end
@@ -260,4 +273,5 @@ module Mobility
260
273
 
261
274
  class BackendRequired < ArgumentError; end
262
275
  class InvalidLocale < I18n::InvalidLocale; end
276
+ class NotImplementedError < StandardError; end
263
277
  end
@@ -7,15 +7,22 @@ Module loading ActiveRecord-specific classes for Mobility models.
7
7
  module ActiveRecord
8
8
  require "mobility/active_record/uniqueness_validator"
9
9
 
10
+ class QueryMethod < Module
11
+ def initialize(query_method)
12
+ module_eval <<-EOM, __FILE__, __LINE__ + 1
13
+ def #{query_method}
14
+ all
15
+ end
16
+ EOM
17
+ end
18
+ end
19
+
10
20
  def self.included(model_class)
11
- query_method = Module.new do
12
- define_method Mobility.query_method do
13
- all
14
- end
21
+ model_class.extend QueryMethod.new(Mobility.query_method)
22
+ unless model_class.const_defined?(:UniquenessValidator)
23
+ model_class.const_set(:UniquenessValidator,
24
+ Class.new(::Mobility::ActiveRecord::UniquenessValidator))
15
25
  end
16
- model_class.extend query_method
17
- model_class.const_set(:UniquenessValidator,
18
- Class.new(::Mobility::ActiveRecord::UniquenessValidator))
19
26
  model_class.delegate :translated_attribute_names, to: :class
20
27
  end
21
28
  end
@@ -1,10 +1,25 @@
1
1
  module Mobility
2
2
  module ActiveRecord
3
+ =begin
4
+
5
+ A backend-agnostic uniqueness validator for ActiveRecord translated attributes.
6
+
7
+ @note This validator does not support case sensitivity, since doing so would
8
+ significantly complicate implementation.
9
+
10
+ =end
3
11
  class UniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
12
+ # @param [ActiveRecord::Base] record Translated model
13
+ # @param [String] attribute Name of attribute
14
+ # @param [Object] value Attribute value
4
15
  def validate_each(record, attribute, value)
5
16
  klass = record.class
6
17
 
7
18
  if ((Array(options[:scope]) + [attribute]).map(&:to_s) & klass.translated_attribute_names).present?
19
+ warn %{
20
+ WARNING: The Mobility uniqueness validator for translated attributes does not
21
+ support case-insensitive validation. This option will be ignored for: #{attribute}
22
+ } if options[:case_sensitive] == false
8
23
  return unless value.present?
9
24
  relation = klass.send(Mobility.query_method).where(attribute => value)
10
25
  relation = relation.where.not(klass.primary_key => record.id) if record.persisted?
@@ -25,10 +40,9 @@ module Mobility
25
40
  private
26
41
 
27
42
  def mobility_scope_relation(record, relation)
28
- Array(options[:scope]).each do |scope_item|
29
- relation = relation.where(scope_item => record.send(scope_item))
43
+ Array(options[:scope]).inject(relation) do |scoped_relation, scope_item|
44
+ scoped_relation.where(scope_item => record.send(scope_item))
30
45
  end
31
- relation
32
46
  end
33
47
  end
34
48
  end
@@ -170,8 +170,11 @@ with other backends.
170
170
 
171
171
  # Process options passed into accessor method before calling backend, and
172
172
  # return locale
173
+ # @deprecated This method was mainly used internally but is no longer
174
+ # needed. It will be removed in the next major release.
173
175
  # @param [Hash] options Options hash passed to accessor method
174
176
  # @return [Symbol] locale
177
+ # TODO: Remove in v1.0
175
178
  def self.process_options!(options)
176
179
  (options[:locale] || Mobility.locale).tap { |locale|
177
180
  Mobility.enforce_available_locales!(locale)
@@ -190,25 +193,45 @@ with other backends.
190
193
  end
191
194
 
192
195
  def define_reader(attribute)
193
- define_method attribute do |**options|
194
- return super() if options.delete(:super)
195
- locale = Mobility::Attributes.process_options!(options)
196
- mobility_backend_for(attribute).read(locale, options)
197
- end
198
-
199
- define_method "#{attribute}?" do |**options|
200
- return super() if options.delete(:super)
201
- locale = Mobility::Attributes.process_options!(options)
202
- mobility_backend_for(attribute).present?(locale, options)
203
- end
196
+ backend = Backend.method_name(attribute)
197
+ class_eval <<-EOM, __FILE__, __LINE__ + 1
198
+ def #{attribute}(**options)
199
+ return super() if options.delete(:super)
200
+ #{set_locale_from_options_inline}
201
+ #{backend}.read(locale, options)
202
+ end
203
+
204
+ def #{attribute}?(**options)
205
+ return super() if options.delete(:super)
206
+ #{set_locale_from_options_inline}
207
+ #{backend}.present?(locale, options)
208
+ end
209
+ EOM
204
210
  end
205
211
 
206
212
  def define_writer(attribute)
207
- define_method "#{attribute}=" do |value, **options|
208
- return super(value) if options.delete(:super)
209
- locale = Mobility::Attributes.process_options!(options)
210
- mobility_backend_for(attribute).write(locale, value, options)
211
- end
213
+ backend = Backend.method_name(attribute)
214
+ class_eval <<-EOM, __FILE__, __LINE__ + 1
215
+ def #{attribute}=(value, **options)
216
+ return super(value) if options.delete(:super)
217
+ #{set_locale_from_options_inline}
218
+ #{backend}.write(locale, value, options)
219
+ end
220
+ EOM
221
+ end
222
+
223
+ # This string is evaluated inline in order to optimize performance of
224
+ # getters and setters, avoiding extra steps where they are unneeded.
225
+ def set_locale_from_options_inline
226
+ <<-EOL
227
+ if options[:locale]
228
+ #{"Mobility.enforce_available_locales!(options[:locale])" if I18n.enforce_available_locales}
229
+ locale = options[:locale].to_sym
230
+ options[:locale] &&= !!locale
231
+ else
232
+ locale = Mobility.locale
233
+ end
234
+ EOL
212
235
  end
213
236
 
214
237
  def get_backend_class(backend)