mobility 0.3.6 → 0.4.0

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