mobility 0.1.12 → 0.1.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile.lock +57 -5
  4. data/README.md +10 -1
  5. data/lib/generators/rails/mobility/install_generator.rb +1 -4
  6. data/lib/generators/rails/mobility/templates/initializer.rb +5 -0
  7. data/lib/mobility.rb +17 -9
  8. data/lib/mobility/active_record.rb +1 -1
  9. data/lib/mobility/attributes.rb +3 -3
  10. data/lib/mobility/backend.rb +2 -2
  11. data/lib/mobility/backend/active_model/dirty.rb +3 -5
  12. data/lib/mobility/backend/active_record.rb +15 -0
  13. data/lib/mobility/backend/active_record/column.rb +5 -12
  14. data/lib/mobility/backend/active_record/column/query_methods.rb +1 -1
  15. data/lib/mobility/backend/active_record/hash_valued.rb +1 -1
  16. data/lib/mobility/backend/active_record/hstore.rb +2 -9
  17. data/lib/mobility/backend/active_record/hstore/query_methods.rb +1 -1
  18. data/lib/mobility/backend/active_record/jsonb.rb +2 -9
  19. data/lib/mobility/backend/active_record/jsonb/query_methods.rb +1 -1
  20. data/lib/mobility/backend/active_record/key_value.rb +6 -11
  21. data/lib/mobility/backend/active_record/key_value/query_methods.rb +1 -1
  22. data/lib/mobility/backend/active_record/query_methods.rb +4 -3
  23. data/lib/mobility/backend/active_record/serialized.rb +5 -10
  24. data/lib/mobility/backend/active_record/serialized/query_methods.rb +1 -1
  25. data/lib/mobility/backend/active_record/table.rb +6 -11
  26. data/lib/mobility/backend/active_record/table/query_methods.rb +1 -1
  27. data/lib/mobility/backend/cache.rb +10 -2
  28. data/lib/mobility/backend/key_value.rb +1 -1
  29. data/lib/mobility/backend/null.rb +1 -1
  30. data/lib/mobility/backend/orm_delegator.rb +3 -3
  31. data/lib/mobility/backend/sequel.rb +15 -0
  32. data/lib/mobility/backend/sequel/column.rb +5 -12
  33. data/lib/mobility/backend/sequel/hash_valued.rb +1 -1
  34. data/lib/mobility/backend/sequel/hstore.rb +2 -9
  35. data/lib/mobility/backend/sequel/jsonb.rb +2 -9
  36. data/lib/mobility/backend/sequel/key_value.rb +6 -11
  37. data/lib/mobility/backend/sequel/serialized.rb +5 -10
  38. data/lib/mobility/backend/sequel/table.rb +5 -10
  39. data/lib/mobility/configuration.rb +5 -0
  40. data/lib/mobility/fallthrough_accessors.rb +2 -1
  41. data/lib/mobility/sequel.rb +1 -1
  42. data/lib/mobility/version.rb +1 -1
  43. metadata +15 -17
  44. data/lib/mobility/instance_methods.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d5bbeab96e1792e6240d3f1854e302c5845689b
4
- data.tar.gz: 0a821c40d9b1c3e6202a92e6194307fb0698c12c
3
+ metadata.gz: 461e43683e242cc5055cd76569443b0d5df9e966
4
+ data.tar.gz: 4f3035a319e5d910d466513a9d90e36a3584edc6
5
5
  SHA512:
6
- metadata.gz: 8cc59378dff919de4592e35b1a73e11ab808616ed1b6a9cdc84737e447a97a50d2503d348a41d3c8645f129a0f65554705a5696041c89950e5e7dd3b98b4f357
7
- data.tar.gz: ed3e03d952e333bd04e4d366b0a9e6d523981e8270c1e5d7d36213e15806383ca3c6c0cd245e72b304420e725bcfbe400f4e3a28b32d64394a7ba29bf4f3004b
6
+ metadata.gz: c5a346cb7c05ac59ecc1cfb834e47e3471c66734a7a0e1655bd4064f51811a07ff789199154102b7123569aff8b6228562127b4b8bf599792d6b88215ded2b7a
7
+ data.tar.gz: 73e5e46907ae6315682f14dc0344180a72a95192808167339928ae10b99d6d4e5f4f1bd7c2b05fbc3922ddd9181ac53fd5fe19de76336ae4270df4b0cdcb8a34
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## 0.1
4
4
 
5
+ ### 0.1.13 (April 19, 2017)
6
+ * Allow passing `cache: false` to disable cache in getter
7
+ ([b4858a](https://github.com/shioyama/mobility/commit/b4858acfb0cf5dae0761672269c248d0e3762bab))
8
+ and setter
9
+ ([6085d7](https://github.com/shioyama/mobility/commit/6085d791a98de7870bdd78fe6b792cbb3f96c1f4))
10
+ * Rename `configure!` method to `configure`
11
+ ([4e35c54](https://github.com/shioyama/mobility/commit/4e35c54cd62033d1ce7b631a1f62efaf4ffa2565))
12
+ * Make query scope method configurable ([#22](https://github.com/shioyama/mobility/pull/22))
13
+ * Do not memoize scopes/datasets ([#24](https://github.com/shioyama/mobility/pull/24))
14
+
5
15
  ### 0.1.12
6
16
  * Extract presence filter into `Mobility::Backend::Presence` class
7
17
  ([7d654](https://github.com/shioyama/mobility/commit/7d65479c832ca154a45a548b64d27016486d34df),
data/Gemfile.lock CHANGED
@@ -1,19 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mobility (0.1.11)
4
+ mobility (0.1.12)
5
5
  i18n (>= 0.6.10)
6
6
  request_store (~> 1.0)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
+ actionpack (5.0.2)
12
+ actionview (= 5.0.2)
13
+ activesupport (= 5.0.2)
14
+ rack (~> 2.0)
15
+ rack-test (~> 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
18
+ actionview (5.0.2)
19
+ activesupport (= 5.0.2)
20
+ builder (~> 3.1)
21
+ erubis (~> 2.7.0)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
24
+ activemodel (5.0.2)
25
+ activesupport (= 5.0.2)
26
+ activerecord (5.0.2)
27
+ activemodel (= 5.0.2)
28
+ activesupport (= 5.0.2)
29
+ arel (~> 7.0)
30
+ activesupport (5.0.2)
31
+ concurrent-ruby (~> 1.0, >= 1.0.2)
32
+ i18n (~> 0.7)
33
+ minitest (~> 5.1)
34
+ tzinfo (~> 1.1)
35
+ arel (7.1.4)
36
+ builder (3.2.3)
11
37
  byebug (9.0.6)
12
38
  coderay (1.1.1)
39
+ concurrent-ruby (1.0.5)
13
40
  database_cleaner (1.5.3)
14
41
  diff-lcs (1.3)
42
+ erubis (2.7.0)
15
43
  ffi (1.9.18)
16
44
  formatador (0.2.5)
45
+ generator_spec (0.9.3)
46
+ activesupport (>= 3.0.0)
47
+ railties (>= 3.0.0)
17
48
  guard (2.14.1)
18
49
  formatador (>= 0.2.4)
19
50
  listen (>= 2.7, < 4.0)
@@ -33,10 +64,16 @@ GEM
33
64
  rb-fsevent (~> 0.9, >= 0.9.4)
34
65
  rb-inotify (~> 0.9, >= 0.9.7)
35
66
  ruby_dep (~> 1.2)
67
+ loofah (2.0.3)
68
+ nokogiri (>= 1.5.9)
36
69
  lumberjack (1.0.11)
37
70
  method_source (0.8.2)
71
+ mini_portile2 (2.1.0)
72
+ minitest (5.10.1)
38
73
  mysql2 (0.3.21)
39
74
  nenv (0.3.0)
75
+ nokogiri (1.7.1)
76
+ mini_portile2 (~> 2.1.0)
40
77
  notiffany (0.1.1)
41
78
  nenv (~> 0.1)
42
79
  shellany (~> 0.0)
@@ -48,6 +85,20 @@ GEM
48
85
  pry-byebug (3.4.2)
49
86
  byebug (~> 9.0)
50
87
  pry (~> 0.10)
88
+ rack (2.0.1)
89
+ rack-test (0.6.3)
90
+ rack (>= 1.0)
91
+ rails-dom-testing (2.0.2)
92
+ activesupport (>= 4.2.0, < 6.0)
93
+ nokogiri (~> 1.6)
94
+ rails-html-sanitizer (1.0.3)
95
+ loofah (~> 2.0)
96
+ railties (5.0.2)
97
+ actionpack (= 5.0.2)
98
+ activesupport (= 5.0.2)
99
+ method_source
100
+ rake (>= 0.8.7)
101
+ thor (>= 0.18.1, < 2.0)
51
102
  rake (10.5.0)
52
103
  rb-fsevent (0.9.8)
53
104
  rb-inotify (0.9.8)
@@ -62,9 +113,6 @@ GEM
62
113
  rspec-expectations (3.5.0)
63
114
  diff-lcs (>= 1.2.0, < 2.0)
64
115
  rspec-support (~> 3.5.0)
65
- rspec-its (1.2.0)
66
- rspec-core (>= 3.0.0)
67
- rspec-expectations (>= 3.0.0)
68
116
  rspec-mocks (3.5.0)
69
117
  diff-lcs (>= 1.2.0, < 2.0)
70
118
  rspec-support (~> 3.5.0)
@@ -74,14 +122,19 @@ GEM
74
122
  slop (3.6.0)
75
123
  sqlite3 (1.3.13)
76
124
  thor (0.19.4)
125
+ thread_safe (0.3.6)
126
+ tzinfo (1.2.3)
127
+ thread_safe (~> 0.1)
77
128
  yard (0.9.8)
78
129
 
79
130
  PLATFORMS
80
131
  ruby
81
132
 
82
133
  DEPENDENCIES
134
+ activerecord (>= 5.0, < 5.1)
83
135
  bundler (~> 1.12)
84
136
  database_cleaner (~> 1.5.3)
137
+ generator_spec (~> 0.9.3)
85
138
  guard-rspec
86
139
  mobility!
87
140
  mysql2 (~> 0.3.10)
@@ -89,7 +142,6 @@ DEPENDENCIES
89
142
  pry-byebug
90
143
  rake (~> 10.0)
91
144
  rspec (~> 3.0)
92
- rspec-its (~> 1.2.0)
93
145
  sqlite3
94
146
  yard (~> 0.9.0)
95
147
 
data/README.md CHANGED
@@ -46,7 +46,7 @@ Installation
46
46
  Add this line to your application's Gemfile:
47
47
 
48
48
  ```ruby
49
- gem 'mobility', '~> 0.1.12'
49
+ gem 'mobility', '~> 0.1.13'
50
50
  ```
51
51
 
52
52
  To translate attributes on a model, include (or extend) `Mobility`, then call
@@ -534,6 +534,9 @@ class Word < ApplicationRecord
534
534
  end
535
535
  ```
536
536
 
537
+ You can also turn off the cache for a single fetch by passing `cache: false` to
538
+ the getter method, i.e. `post.title(cache: false)`.
539
+
537
540
  The cache is normally just a hash with locale keys and string (translation)
538
541
  values, but some backends (e.g. KeyValue and Table backends) have slightly more
539
542
  complex implementations.
@@ -758,6 +761,12 @@ can be changed, see the shared examples for details.
758
761
  Backends are also each tested against specialized specs targeted at their
759
762
  particular implementations.
760
763
 
764
+ Integrations
765
+ ------------
766
+
767
+ * [friendly_id-mobility](https://github.com/shioyama/friendly_id-mobility): Use
768
+ Mobility with [FriendlyId](https://github.com/norman/friendly_id).
769
+
761
770
  More Information
762
771
  ----------------
763
772
 
@@ -21,10 +21,7 @@ module Mobility
21
21
  end
22
22
 
23
23
  def create_initializer
24
- create_file(
25
- "config/initializers/mobility.rb",
26
- "Mobility.configure do |config|\n config.default_backend = :key_value\n config.accessor_method = :translates\nend"
27
- )
24
+ copy_file "initializer.rb", "config/initializers/mobility.rb"
28
25
  end
29
26
 
30
27
  def self.next_migration_number(dirname)
@@ -0,0 +1,5 @@
1
+ Mobility.configure do |config|
2
+ config.default_backend = :key_value
3
+ config.accessor_method = :translates
4
+ config.query_method = :i18n
5
+ end
data/lib/mobility.rb CHANGED
@@ -38,7 +38,6 @@ module Mobility
38
38
  autoload :Configuration, "mobility/configuration"
39
39
  autoload :FallthroughAccessors, "mobility/fallthrough_accessors"
40
40
  autoload :LocaleAccessors, "mobility/locale_accessors"
41
- autoload :InstanceMethods, "mobility/instance_methods"
42
41
  autoload :Translates, "mobility/translates"
43
42
  autoload :Wrapper, "mobility/wrapper"
44
43
 
@@ -81,23 +80,29 @@ module Mobility
81
80
  def extended(model_class)
82
81
  return if model_class.respond_to? :mobility_accessor
83
82
  model_class.class_eval do
84
- def self.mobility
85
- @mobility ||= Mobility::Wrapper.new(self)
86
- end
87
- def self.translated_attribute_names
88
- mobility.translated_attribute_names
83
+ # Fetch backend for an attribute
84
+ # @param [String] attribute Attribute
85
+ def mobility_backend_for(attribute)
86
+ send(Backend.method_name(attribute))
89
87
  end
90
88
 
91
89
  class << self
92
90
  include Translates
91
+
93
92
  if translates = Mobility.config.accessor_method
94
93
  alias_method translates, :mobility_accessor
95
94
  end
95
+
96
+ def mobility
97
+ @mobility ||= Mobility::Wrapper.new(self)
98
+ end
99
+
100
+ def translated_attribute_names
101
+ mobility.translated_attribute_names
102
+ end
96
103
  end
97
104
  end
98
105
 
99
- model_class.include(InstanceMethods)
100
-
101
106
  if Loaded::ActiveRecord
102
107
  model_class.include(ActiveRecord) if model_class < ::ActiveRecord::Base
103
108
  model_class.include(ActiveModel::AttributeMethods) if model_class.ancestors.include?(::ActiveModel::AttributeMethods)
@@ -157,6 +162,9 @@ module Mobility
157
162
 
158
163
  # (see Mobility::Configuration#accessor_method)
159
164
  # @!method accessor_method
165
+ #
166
+ # (see Mobility::Configuration#query_method)
167
+ # @!method query_method
160
168
 
161
169
  # (see Mobility::Configuration#default_fallbacks)
162
170
  # @!method default_fallbacks
@@ -166,7 +174,7 @@ module Mobility
166
174
 
167
175
  # (see Mobility::Configuration#default_accessor_locales)
168
176
  # @!method default_accessor_locales
169
- %w[accessor_method default_backend default_accessor_locales].each do |method_name|
177
+ %w[accessor_method query_method default_backend default_accessor_locales].each do |method_name|
170
178
  define_method method_name do
171
179
  config.public_send(method_name)
172
180
  end
@@ -31,7 +31,7 @@ Module loading ActiveRecord-specific classes for Mobility models.
31
31
 
32
32
  module ClassMethods
33
33
  # @return [ActiveRecord::Relation] relation extended with Mobility query methods.
34
- def i18n
34
+ define_method ::Mobility.query_method do
35
35
  all
36
36
  end
37
37
  end
@@ -84,8 +84,8 @@ This allows a backend to do things like (for example) define associations on a
84
84
  model class required by the backend, as happens in the {Backend::KeyValue} and
85
85
  {Backend::Table} backends.
86
86
 
87
- The +setup+ block is also used to extend the +i18n+ scope/dataset with
88
- backend-specific query method support.
87
+ The +setup+ block is also used to extend the query scope/dataset (+i18n+ by
88
+ default) with backend-specific query method support.
89
89
 
90
90
  Since setup blocks are evaluated on the model class, it is possible that
91
91
  backends can conflict (for example, overwriting previously defined methods).
@@ -141,7 +141,7 @@ with other backends.
141
141
  end
142
142
  include FallthroughAccessors.new(*attributes) if options[:fallthrough_accessors]
143
143
 
144
- @backend_class.configure!(options) if @backend_class.respond_to?(:configure!)
144
+ @backend_class.configure(options) if @backend_class.respond_to?(:configure)
145
145
 
146
146
  include_backend_modules(@backend_class, options)
147
147
 
@@ -18,7 +18,7 @@ On top of this, a backend will normally:
18
18
 
19
19
  - implement a +read+ instance method to read from the backend
20
20
  - implement a +write+ instance method to write to the backend
21
- - implement a +configure!+ class method to apply any normalization to the
21
+ - implement a +configure+ class method to apply any normalization to the
22
22
  options hash
23
23
  - call the +setup+ method yielding attributes and options to configure the
24
24
  model class
@@ -35,7 +35,7 @@ On top of this, a backend will normally:
35
35
  # ...
36
36
  end
37
37
 
38
- def self.configure!(options)
38
+ def self.configure(options)
39
39
  # ...
40
40
  end
41
41
 
@@ -86,11 +86,9 @@ value of the translated attribute if passed to it.
86
86
  # they change from Rails version to version.
87
87
  def self.method_suffixes
88
88
  @method_suffixes ||=
89
- begin
90
- Class.new do
91
- include ::ActiveModel::Dirty
92
- end.attribute_method_matchers.map { |m| m.suffix }
93
- end.select { |m| m =~ /\A_/ }
89
+ Class.new do
90
+ include ::ActiveModel::Dirty
91
+ end.attribute_method_matchers.map(&:suffix).select { |m| m =~ /\A_/ }
94
92
  end
95
93
  end
96
94
  end
@@ -8,6 +8,21 @@ module Mobility
8
8
  autoload :Serialized, 'mobility/backend/active_record/serialized'
9
9
  autoload :QueryMethods, 'mobility/backend/active_record/query_methods'
10
10
  autoload :Table, 'mobility/backend/active_record/table'
11
+
12
+ def setup_query_methods(query_methods)
13
+ setup do |attributes, options|
14
+ extend(Module.new do
15
+ define_method ::Mobility.query_method do
16
+ super().extending(query_methods.new(attributes, options))
17
+ end
18
+ end)
19
+ end
20
+ end
21
+
22
+ def self.included(backend_class)
23
+ backend_class.include(Backend)
24
+ backend_class.extend(self)
25
+ end
11
26
  end
12
27
  end
13
28
  end
@@ -29,10 +29,10 @@ or locales.)
29
29
  #=> "foo"
30
30
  =end
31
31
  class ActiveRecord::Column
32
- include Backend
33
- include Backend::Column
32
+ include ActiveRecord
33
+ include Column
34
34
 
35
- autoload :QueryMethods, 'mobility/backend/active_record/column/query_methods'
35
+ require 'mobility/backend/active_record/column/query_methods'
36
36
 
37
37
  # @!group Backend Accessors
38
38
  # @!macro backend_reader
@@ -47,19 +47,12 @@ or locales.)
47
47
  end
48
48
 
49
49
  # @!group Backend Configuration
50
- def self.configure!(options)
50
+ def self.configure(options)
51
51
  options[:locale_accessors] = false
52
52
  end
53
53
  # @!endgroup
54
54
 
55
- setup do |attributes, options|
56
- mod = Module.new do
57
- define_method :i18n do
58
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
59
- end
60
- end
61
- extend mod
62
- end
55
+ setup_query_methods(QueryMethods)
63
56
  end
64
57
  end
65
58
  end
@@ -32,7 +32,7 @@ module Mobility
32
32
  super(opts_converter.call(opts), *rest)
33
33
  end
34
34
  end
35
- relation.model.mobility_where_chain.prepend(mod)
35
+ relation.mobility_where_chain.include(mod)
36
36
  end
37
37
  end
38
38
  end
@@ -6,7 +6,7 @@ Internal class used by ActiveRecord backends that store values as a hash.
6
6
 
7
7
  =end
8
8
  class ActiveRecord::HashValued
9
- include Backend
9
+ include ActiveRecord
10
10
 
11
11
  # @!group Backend Accessors
12
12
  #
@@ -10,7 +10,7 @@ Implements the {Mobility::Backend::Hstore} backend for ActiveRecord models.
10
10
 
11
11
  =end
12
12
  class ActiveRecord::Hstore < ActiveRecord::HashValued
13
- autoload :QueryMethods, 'mobility/backend/active_record/hstore/query_methods'
13
+ require 'mobility/backend/active_record/hstore/query_methods'
14
14
 
15
15
  # @!group Backend Accessors
16
16
  # @!macro backend_reader
@@ -23,14 +23,7 @@ Implements the {Mobility::Backend::Hstore} backend for ActiveRecord models.
23
23
  end
24
24
  # @!endgroup
25
25
 
26
- setup do |attributes, options|
27
- query_methods = Module.new do
28
- define_method :i18n do
29
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
30
- end
31
- end
32
- extend query_methods
33
- end
26
+ setup_query_methods(QueryMethods)
34
27
  end
35
28
  end
36
29
  end
@@ -59,7 +59,7 @@ module Mobility
59
59
  end
60
60
  end
61
61
  end
62
- relation.model.mobility_where_chain.prepend(mod)
62
+ relation.mobility_where_chain.include(mod)
63
63
  end
64
64
  end
65
65
  end
@@ -10,7 +10,7 @@ Implements the {Mobility::Backend::Jsonb} backend for ActiveRecord models.
10
10
 
11
11
  =end
12
12
  class ActiveRecord::Jsonb < ActiveRecord::HashValued
13
- autoload :QueryMethods, 'mobility/backend/active_record/jsonb/query_methods'
13
+ require 'mobility/backend/active_record/jsonb/query_methods'
14
14
 
15
15
  # @!group Backend Accessors
16
16
  #
@@ -30,14 +30,7 @@ Implements the {Mobility::Backend::Jsonb} backend for ActiveRecord models.
30
30
  # @return [String,Integer,Boolean] Updated value
31
31
  # @!method write(locale, value, **options)
32
32
 
33
- setup do |attributes, options|
34
- query_methods = Module.new do
35
- define_method :i18n do
36
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
37
- end
38
- end
39
- extend query_methods
40
- end
33
+ setup_query_methods(QueryMethods)
41
34
  end
42
35
  end
43
36
  end
@@ -60,7 +60,7 @@ module Mobility
60
60
  end
61
61
  end
62
62
  end
63
- relation.model.mobility_where_chain.prepend(mod)
63
+ relation.mobility_where_chain.include(mod)
64
64
  end
65
65
  end
66
66
  end
@@ -21,10 +21,10 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
21
21
 
22
22
  =end
23
23
  class ActiveRecord::KeyValue
24
- include Backend
25
- include Backend::KeyValue
24
+ include ActiveRecord
25
+ include KeyValue
26
26
 
27
- autoload :QueryMethods, 'mobility/backend/active_record/key_value/query_methods'
27
+ require 'mobility/backend/active_record/key_value/query_methods'
28
28
 
29
29
  # @return [Symbol] Name of the association
30
30
  attr_reader :association_name
@@ -53,7 +53,7 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
53
53
  # @option options [Symbol] association_name (:mobility_text_translations) Name of association method
54
54
  # @option options [String,Class] class_name ({Mobility::ActiveRecord::TextTranslation}) Translation class
55
55
  # @raise [ArgumentError] if type is not either :text or :string
56
- def self.configure!(options)
56
+ def self.configure(options)
57
57
  super
58
58
  type = options[:type]
59
59
  options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation".freeze)
@@ -88,13 +88,6 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
88
88
  end
89
89
  after_destroy :mobility_destroy_key_value_translations
90
90
 
91
- mod = Module.new do
92
- define_method :i18n do
93
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
94
- end
95
- end
96
- extend mod
97
-
98
91
  private
99
92
 
100
93
  # Clean up *all* leftover translations of this model, only once.
@@ -106,6 +99,8 @@ Implements the {Mobility::Backend::KeyValue} backend for ActiveRecord models.
106
99
  end unless private_instance_methods(false).include?(:mobility_destroy_key_value_translations)
107
100
  end
108
101
 
102
+ setup_query_methods(QueryMethods)
103
+
109
104
  # @!group Cache Methods
110
105
  # @return [KeyValue::TranslationsCache]
111
106
  def new_cache
@@ -31,7 +31,7 @@ module Mobility
31
31
  end
32
32
  end
33
33
  end
34
- relation.model.mobility_where_chain.prepend(mod)
34
+ relation.mobility_where_chain.include(mod)
35
35
  end
36
36
 
37
37
  private
@@ -17,10 +17,11 @@ models. For details see backend-specific subclasses.
17
17
  end
18
18
 
19
19
  # @param [ActiveRecord::Relation] relation Relation being extended
20
+ # @note Only want to define this once, even if multiple QueryMethods
21
+ # modules are included, so define it here in extended method
20
22
  def extended(relation)
21
- model_class = relation.model
22
- unless model_class.respond_to?(:mobility_where_chain)
23
- model_class.define_singleton_method(:mobility_where_chain) do
23
+ unless relation.respond_to?(:mobility_where_chain)
24
+ relation.define_singleton_method(:mobility_where_chain) do
24
25
  @mobility_where_chain ||= Class.new(::ActiveRecord::QueryMethods::WhereChain)
25
26
  end
26
27
 
@@ -21,9 +21,9 @@ Implements {Mobility::Backend::Serialized} backend for ActiveRecord models.
21
21
 
22
22
  =end
23
23
  class ActiveRecord::Serialized
24
- include Backend
24
+ include ActiveRecord
25
25
 
26
- autoload :QueryMethods, 'mobility/backend/active_record/serialized/query_methods'
26
+ require 'mobility/backend/active_record/serialized/query_methods'
27
27
 
28
28
  # @!group Backend Accessors
29
29
  #
@@ -41,7 +41,7 @@ Implements {Mobility::Backend::Serialized} backend for ActiveRecord models.
41
41
  # @!group Backend Configuration
42
42
  # @option options [Symbol] format (:yaml) Serialization format
43
43
  # @raise [ArgumentError] if a format other than +:yaml+ or +:json+ is passed in
44
- def self.configure!(options)
44
+ def self.configure(options)
45
45
  options[:format] ||= :yaml
46
46
  options[:format] = options[:format].downcase.to_sym
47
47
  raise ArgumentError, "Serialized backend only supports yaml or json formats." unless [:yaml, :json].include?(options[:format])
@@ -51,15 +51,10 @@ Implements {Mobility::Backend::Serialized} backend for ActiveRecord models.
51
51
  setup do |attributes, options|
52
52
  coder = { yaml: YAMLCoder, json: JSONCoder }[options[:format]]
53
53
  attributes.each { |attribute| serialize attribute, coder }
54
-
55
- extension = Module.new do
56
- define_method :i18n do
57
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
58
- end
59
- end
60
- extend extension
61
54
  end
62
55
 
56
+ setup_query_methods(QueryMethods)
57
+
63
58
  # @!group Cache Methods
64
59
  # Returns column value as a hash
65
60
  # @return [Hash]
@@ -25,7 +25,7 @@ module Mobility
25
25
  opts_checker.call(opts) || super(opts, *rest)
26
26
  end
27
27
  end
28
- relation.model.mobility_where_chain.prepend(mod)
28
+ relation.mobility_where_chain.include(mod)
29
29
  end
30
30
  end
31
31
  end
@@ -84,9 +84,9 @@ columns to that table.
84
84
  # title: "bar">
85
85
  =end
86
86
  class ActiveRecord::Table
87
- include Backend
87
+ include ActiveRecord
88
88
 
89
- autoload :QueryMethods, 'mobility/backend/active_record/table/query_methods'
89
+ require 'mobility/backend/active_record/table/query_methods'
90
90
 
91
91
  # @return [Symbol] name of the association method
92
92
  attr_reader :association_name
@@ -117,7 +117,7 @@ columns to that table.
117
117
  # @option options [Symbol] foreign_key Name of foreign key
118
118
  # @option options [Symbol] subclass_name (:Translation) Name of subclass
119
119
  # to append to model class to generate translation class
120
- def self.configure!(options)
120
+ def self.configure(options)
121
121
  table_name = options[:model_class].table_name
122
122
  options[:table_name] ||= "#{table_name.singularize}_translations".freeze
123
123
  options[:foreign_key] ||= table_name.downcase.singularize.camelize.foreign_key
@@ -131,7 +131,7 @@ columns to that table.
131
131
  end
132
132
  # @!endgroup
133
133
 
134
- setup do |attributes, options|
134
+ setup do |_attributes, options|
135
135
  association_name = options[:association_name]
136
136
  subclass_name = options[:subclass_name]
137
137
 
@@ -157,15 +157,10 @@ columns to that table.
157
157
  class_name: name,
158
158
  foreign_key: options[:foreign_key],
159
159
  inverse_of: association_name
160
-
161
- query_methods = Module.new do
162
- define_method :i18n do
163
- @mobility_scope ||= super().extending(QueryMethods.new(attributes, options))
164
- end
165
- end
166
- extend query_methods
167
160
  end
168
161
 
162
+ setup_query_methods(QueryMethods)
163
+
169
164
  # @!group Cache Methods
170
165
  # @return [Table::TranslationsCache]
171
166
  def new_cache
@@ -34,7 +34,7 @@ module Mobility
34
34
  end
35
35
  end
36
36
  end
37
- relation.model.mobility_where_chain.prepend(mod)
37
+ relation.mobility_where_chain.include(mod)
38
38
  end
39
39
 
40
40
  private
@@ -35,7 +35,11 @@ this).
35
35
  module Cache
36
36
  # @group Backend Accessors
37
37
  # @!macro backend_reader
38
- def read(locale, **_)
38
+ # @param [Hash] options
39
+ # @param [Boolean] cache
40
+ # *false* to disable cache.
41
+ def read(locale, **options)
42
+ return super if options.delete(:cache) == false
39
43
  if write_to_cache? || cache.has_key?(locale)
40
44
  cache[locale]
41
45
  else
@@ -44,7 +48,11 @@ this).
44
48
  end
45
49
 
46
50
  # @!macro backend_writer
47
- def write(locale, value, **_)
51
+ # @param [Hash] options
52
+ # @param [Boolean] cache
53
+ # *false* to disable cache.
54
+ def write(locale, value, **options)
55
+ return super if options.delete(:cache) == false
48
56
  cache[locale] = write_to_cache? ? value : super
49
57
  end
50
58
  # @!endgroup
@@ -49,7 +49,7 @@ class.
49
49
  # @!group Backend Configuration
50
50
  # @option options [Symbol,String] type (:text) Column type to use
51
51
  # @raise [ArgumentError] if type is not either :text or :string
52
- def configure!(options)
52
+ def configure(options)
53
53
  options[:type] = (options[:type] || :text).to_sym
54
54
  raise ArgumentError, "type must be one of: [text, string]" unless [:text, :string].include?(options[:type])
55
55
  end
@@ -17,7 +17,7 @@ Backend which does absolutely nothing. Mostly for testing purposes.
17
17
  # @!endgroup
18
18
 
19
19
  # @!group Backend Configuration
20
- def self.configure!(*); end
20
+ def self.configure(*); end
21
21
  # @!endgroup
22
22
  end
23
23
  end
@@ -17,11 +17,11 @@ Adds {#for} method to backend to return ORM-specific backend.
17
17
  # @return [Class] Class of backend to use for model
18
18
  def for(model_class)
19
19
  if Loaded::ActiveRecord && model_class < ::ActiveRecord::Base
20
- const_get(name.split("::").insert(-2, "ActiveRecord").join("::"))
20
+ const_get(name.split("::".freeze).insert(-2, "ActiveRecord".freeze).join("::".freeze))
21
21
  elsif Loaded::Sequel && model_class < ::Sequel::Model
22
- const_get(name.split("::").insert(-2, "Sequel").join("::"))
22
+ const_get(name.split("::".freeze).insert(-2, "Sequel".freeze).join("::".freeze))
23
23
  else
24
- raise ArgumentError, "#{name.split('::').last} backend can only be used by ActiveRecord or Sequel models"
24
+ raise ArgumentError, "#{name.split('::'.freeze).last} backend can only be used by ActiveRecord or Sequel models".freeze
25
25
  end
26
26
  end
27
27
 
@@ -9,6 +9,21 @@ module Mobility
9
9
  autoload :Serialized, 'mobility/backend/sequel/serialized'
10
10
  autoload :Table, 'mobility/backend/sequel/table'
11
11
  autoload :QueryMethods, 'mobility/backend/sequel/query_methods'
12
+
13
+ def setup_query_methods(query_methods)
14
+ setup do |attributes, options|
15
+ extend(Module.new do
16
+ define_method ::Mobility.query_method do
17
+ super().with_extend(query_methods.new(attributes, options))
18
+ end
19
+ end)
20
+ end
21
+ end
22
+
23
+ def self.included(backend_class)
24
+ backend_class.include(Backend)
25
+ backend_class.extend(self)
26
+ end
12
27
  end
13
28
  end
14
29
  end
@@ -8,10 +8,10 @@ Implements the {Mobility::Backend::Column} backend for Sequel models.
8
8
  otherwise interfere with column methods.
9
9
  =end
10
10
  class Sequel::Column
11
- include Backend
12
- include Backend::Column
11
+ include Sequel
12
+ include Column
13
13
 
14
- autoload :QueryMethods, 'mobility/backend/sequel/column/query_methods'
14
+ require 'mobility/backend/sequel/column/query_methods'
15
15
 
16
16
  # @!group Backend Accessors
17
17
  # @!macro backend_reader
@@ -28,19 +28,12 @@ Implements the {Mobility::Backend::Column} backend for Sequel models.
28
28
  end
29
29
 
30
30
  # @!group Backend Configuration
31
- def self.configure!(options)
31
+ def self.configure(options)
32
32
  options[:locale_accessors] = false
33
33
  end
34
34
  # @!endgroup
35
35
 
36
- setup do |attributes, options|
37
- extension = Module.new do
38
- define_method :i18n do
39
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
40
- end
41
- end
42
- extend extension
43
- end
36
+ setup_query_methods(QueryMethods)
44
37
  end
45
38
  end
46
39
  end
@@ -6,7 +6,7 @@ Internal class used by Sequel backends that store values as a hash.
6
6
 
7
7
  =end
8
8
  class Sequel::HashValued
9
- include Backend
9
+ include Sequel
10
10
 
11
11
  # @!macro backend_reader
12
12
  def read(locale, **_)
@@ -10,7 +10,7 @@ Implements the {Mobility::Backend::Hstore} backend for Sequel models.
10
10
 
11
11
  =end
12
12
  class Sequel::Hstore < Sequel::HashValued
13
- autoload :QueryMethods, 'mobility/backend/sequel/hstore/query_methods'
13
+ require 'mobility/backend/sequel/hstore/query_methods'
14
14
 
15
15
  # @!group Backend Accessors
16
16
  # @!macro backend_reader
@@ -23,14 +23,7 @@ Implements the {Mobility::Backend::Hstore} backend for Sequel models.
23
23
  end
24
24
  # @!endgroup
25
25
 
26
- setup do |attributes, options|
27
- extension = Module.new do
28
- define_method :i18n do
29
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
30
- end
31
- end
32
- extend extension
33
- end
26
+ setup_query_methods(QueryMethods)
34
27
  end
35
28
  end
36
29
  end
@@ -10,7 +10,7 @@ Implements the {Mobility::Backend::Jsonb} backend for Sequel models.
10
10
 
11
11
  =end
12
12
  class Sequel::Jsonb < Sequel::HashValued
13
- autoload :QueryMethods, 'mobility/backend/sequel/jsonb/query_methods'
13
+ require 'mobility/backend/sequel/jsonb/query_methods'
14
14
 
15
15
  # @!group Backend Accessors
16
16
  #
@@ -30,14 +30,7 @@ Implements the {Mobility::Backend::Jsonb} backend for Sequel models.
30
30
  # @return [String,Integer,Boolean] Updated value
31
31
  # @!method write(locale, value, **options)
32
32
 
33
- setup do |attributes, options|
34
- extension = Module.new do
35
- define_method :i18n do
36
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
37
- end
38
- end
39
- extend extension
40
- end
33
+ setup_query_methods(QueryMethods)
41
34
  end
42
35
  end
43
36
  end
@@ -12,10 +12,10 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
12
12
 
13
13
  =end
14
14
  class Sequel::KeyValue
15
- include Backend
16
- include Backend::KeyValue
15
+ include Sequel
16
+ include KeyValue
17
17
 
18
- autoload :QueryMethods, 'mobility/backend/sequel/key_value/query_methods'
18
+ require 'mobility/backend/sequel/key_value/query_methods'
19
19
 
20
20
  # @return [Symbol] name of the association
21
21
  attr_reader :association_name
@@ -50,7 +50,7 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
50
50
  # @option options [Symbol] class_name ({Mobility::Sequel::TextTranslation}) Translation class
51
51
  # @raise [CacheRequired] if cache is disabled
52
52
  # @raise [ArgumentError] if type is not either :text or :string
53
- def self.configure!(options)
53
+ def self.configure(options)
54
54
  super
55
55
  raise CacheRequired, "Cache required for Sequel::KeyValue backend" if options[:cache] == false
56
56
  type = options[:type]
@@ -91,13 +91,6 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
91
91
  end
92
92
  include callback_methods
93
93
 
94
- extension = Module.new do
95
- define_method :i18n do
96
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
97
- end
98
- end
99
- extend extension
100
-
101
94
  include Mobility::Sequel::ColumnChanges.new(attributes)
102
95
 
103
96
  private
@@ -117,6 +110,8 @@ Implements the {Mobility::Backend::KeyValue} backend for Sequel models.
117
110
  include mobility_key_value_callbacks_module
118
111
  end
119
112
 
113
+ setup_query_methods(QueryMethods)
114
+
120
115
  # @!group Cache Methods
121
116
  # @return [KeyValue::TranslationsCache]
122
117
  def new_cache
@@ -28,9 +28,9 @@ Sequel serialization plugin.
28
28
 
29
29
  =end
30
30
  class Sequel::Serialized
31
- include Backend
31
+ include Sequel
32
32
 
33
- autoload :QueryMethods, 'mobility/backend/sequel/serialized/query_methods'
33
+ require 'mobility/backend/sequel/serialized/query_methods'
34
34
 
35
35
  # @!group Backend Accessors
36
36
  #
@@ -48,7 +48,7 @@ Sequel serialization plugin.
48
48
  # @!group Backend Configuration
49
49
  # @option options [Symbol] format (:yaml) Serialization format
50
50
  # @raise [ArgumentError] if a format other than +:yaml+ or +:json+ is passed in
51
- def self.configure!(options)
51
+ def self.configure(options)
52
52
  options[:format] ||= :yaml
53
53
  options[:format] = options[:format].downcase.to_sym
54
54
  raise ArgumentError, "Serialized backend only supports yaml or json formats." unless [:yaml, :json].include?(options[:format])
@@ -74,16 +74,11 @@ Sequel serialization plugin.
74
74
  end
75
75
  include method_overrides
76
76
 
77
- extension = Module.new do
78
- define_method :i18n do
79
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
80
- end
81
- end
82
- extend extension
83
-
84
77
  include SerializationModificationDetectionFix
85
78
  end
86
79
 
80
+ setup_query_methods(QueryMethods)
81
+
87
82
  # Returns deserialized column value
88
83
  # @return [Hash]
89
84
  def translations
@@ -6,9 +6,9 @@ Implements the {Mobility::Backend::Table} backend for Sequel models.
6
6
 
7
7
  =end
8
8
  class Sequel::Table
9
- include Backend
9
+ include Sequel
10
10
 
11
- autoload :QueryMethods, 'mobility/backend/sequel/table/query_methods'
11
+ require 'mobility/backend/sequel/table/query_methods'
12
12
 
13
13
  # @return [Symbol] name of the association method
14
14
  attr_reader :association_name
@@ -41,7 +41,7 @@ Implements the {Mobility::Backend::Table} backend for Sequel models.
41
41
  # @option options [Symbol] foreign_key Name of foreign key
42
42
  # @option options [Symbol] subclass_name Name of subclass to append to model class to generate translation class
43
43
  # @raise [CacheRequired] if cache option is false
44
- def self.configure!(options)
44
+ def self.configure(options)
45
45
  raise CacheRequired, "Cache required for Sequel::Table backend" if options[:cache] == false
46
46
  table_name = options[:model_class].table_name
47
47
  options[:table_name] ||= :"#{table_name.to_s.singularize}_translations"
@@ -95,16 +95,11 @@ Implements the {Mobility::Backend::Table} backend for Sequel models.
95
95
  end
96
96
  include callback_methods
97
97
 
98
- extension = Module.new do
99
- define_method :i18n do
100
- @mobility_scope ||= super().with_extend(QueryMethods.new(attributes, options))
101
- end
102
- end
103
- extend extension
104
-
105
98
  include Mobility::Sequel::ColumnChanges.new(attributes)
106
99
  end
107
100
 
101
+ setup_query_methods(QueryMethods)
102
+
108
103
  # @!group Cache Methods
109
104
  # @return [Table::TranslationsCache]
110
105
  def new_cache
@@ -9,6 +9,10 @@ Stores shared Mobility configuration referenced by all backends.
9
9
  # @return [Symbol]
10
10
  attr_accessor :accessor_method
11
11
 
12
+ # Name of query scope/dataset method (defaults to +i18n+)
13
+ # @return [Symbol]
14
+ attr_accessor :query_method
15
+
12
16
  # Default fallbacks instance
13
17
  # @return [I18n::Locale::Fallbacks]
14
18
  def default_fallbacks(fallbacks = {})
@@ -34,6 +38,7 @@ Stores shared Mobility configuration referenced by all backends.
34
38
 
35
39
  def initialize
36
40
  @accessor_method = :translates
41
+ @query_method = :i18n
37
42
  @default_fallbacks = lambda { |fallbacks| I18n::Locale::Fallbacks.new(fallbacks) }
38
43
  @default_accessor_locales = lambda { I18n.available_locales }
39
44
  end
@@ -38,7 +38,8 @@ model class is generated.
38
38
  def initialize(*attributes)
39
39
  method_name_regex = /\A(#{attributes.join('|'.freeze)})_([a-z]{2}(_[a-z]{2})?)(=?|\??)\z/.freeze
40
40
 
41
- define_method :method_missing do |method_name, *arguments, &block| if method_name =~ method_name_regex
41
+ define_method :method_missing do |method_name, *arguments, &block|
42
+ if method_name =~ method_name_regex
42
43
  attribute = $1.to_sym
43
44
  locale, suffix = $2.split('_'.freeze)
44
45
  locale = "#{locale}-#{suffix.upcase}".freeze if suffix
@@ -18,7 +18,7 @@ Module loading Sequel-specific classes for Mobility models.
18
18
 
19
19
  module ClassMethods
20
20
  # @return [Sequel::Dataset] dataset extended with Mobility query methods.
21
- def i18n
21
+ define_method ::Mobility.query_method do
22
22
  dataset
23
23
  end
24
24
  end
@@ -1,3 +1,3 @@
1
1
  module Mobility
2
- VERSION = "0.1.12"
2
+ VERSION = "0.1.13"
3
3
  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.12
4
+ version: 0.1.13
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-04-04 00:00:00.000000000 Z
11
+ date: 2017-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: request_store
@@ -31,6 +31,9 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.6.10
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '0.9'
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,6 +41,9 @@ dependencies:
38
41
  - - ">="
39
42
  - !ruby/object:Gem::Version
40
43
  version: 0.6.10
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: bundler
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -57,6 +63,9 @@ dependencies:
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.5'
68
+ - - ">="
60
69
  - !ruby/object:Gem::Version
61
70
  version: 1.5.3
62
71
  type: :development
@@ -64,6 +73,9 @@ dependencies:
64
73
  version_requirements: !ruby/object:Gem::Requirement
65
74
  requirements:
66
75
  - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '1.5'
78
+ - - ">="
67
79
  - !ruby/object:Gem::Version
68
80
  version: 1.5.3
69
81
  - !ruby/object:Gem::Dependency
@@ -94,20 +106,6 @@ dependencies:
94
106
  - - "~>"
95
107
  - !ruby/object:Gem::Version
96
108
  version: '3.0'
97
- - !ruby/object:Gem::Dependency
98
- name: rspec-its
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 1.2.0
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 1.2.0
111
109
  - !ruby/object:Gem::Dependency
112
110
  name: yard
113
111
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +143,7 @@ files:
145
143
  - lib/generators/rails/mobility/templates/column_translations.rb
146
144
  - lib/generators/rails/mobility/templates/create_string_translations.rb
147
145
  - lib/generators/rails/mobility/templates/create_text_translations.rb
146
+ - lib/generators/rails/mobility/templates/initializer.rb
148
147
  - lib/generators/rails/mobility/templates/table_migration.rb
149
148
  - lib/generators/rails/mobility/templates/table_translations.rb
150
149
  - lib/generators/rails/mobility/translations_generator.rb
@@ -210,7 +209,6 @@ files:
210
209
  - lib/mobility/core_ext/object.rb
211
210
  - lib/mobility/core_ext/string.rb
212
211
  - lib/mobility/fallthrough_accessors.rb
213
- - lib/mobility/instance_methods.rb
214
212
  - lib/mobility/locale_accessors.rb
215
213
  - lib/mobility/orm.rb
216
214
  - lib/mobility/rails.rb
@@ -1,15 +0,0 @@
1
- module Mobility
2
- =begin
3
-
4
- Instance methods attached to all model classes when model includes or extends
5
- {Mobility}.
6
-
7
- =end
8
- module InstanceMethods
9
- # Fetch backend for an attribute
10
- # @param [String] attribute Attribute
11
- def mobility_backend_for(attribute)
12
- send(Backend.method_name(attribute))
13
- end
14
- end
15
- end