mobility 1.0.0.alpha → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) 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 +54 -1
  5. data/Gemfile +5 -16
  6. data/Gemfile.lock +30 -82
  7. data/README.md +24 -29
  8. data/lib/mobility.rb +67 -9
  9. data/lib/mobility/backend.rb +8 -10
  10. data/lib/mobility/backends.rb +1 -1
  11. data/lib/mobility/backends/active_record.rb +1 -1
  12. data/lib/mobility/backends/active_record/column.rb +1 -1
  13. data/lib/mobility/backends/active_record/container.rb +6 -9
  14. data/lib/mobility/backends/active_record/hstore.rb +4 -4
  15. data/lib/mobility/backends/active_record/json.rb +3 -3
  16. data/lib/mobility/backends/active_record/jsonb.rb +3 -3
  17. data/lib/mobility/backends/active_record/key_value.rb +27 -11
  18. data/lib/mobility/backends/active_record/serialized.rb +4 -0
  19. data/lib/mobility/backends/active_record/table.rb +12 -7
  20. data/lib/mobility/backends/container.rb +10 -2
  21. data/lib/mobility/backends/hash_valued.rb +4 -0
  22. data/lib/mobility/backends/jsonb.rb +1 -1
  23. data/lib/mobility/backends/key_value.rb +12 -15
  24. data/lib/mobility/backends/sequel.rb +34 -2
  25. data/lib/mobility/backends/sequel/container.rb +8 -8
  26. data/lib/mobility/backends/sequel/hstore.rb +1 -1
  27. data/lib/mobility/backends/sequel/json.rb +1 -0
  28. data/lib/mobility/backends/sequel/key_value.rb +79 -12
  29. data/lib/mobility/backends/sequel/pg_hash.rb +6 -6
  30. data/lib/mobility/backends/sequel/serialized.rb +4 -0
  31. data/lib/mobility/backends/sequel/table.rb +18 -8
  32. data/lib/mobility/backends/table.rb +29 -29
  33. data/lib/mobility/pluggable.rb +21 -1
  34. data/lib/mobility/plugin.rb +2 -2
  35. data/lib/mobility/plugins.rb +2 -0
  36. data/lib/mobility/plugins/active_model/dirty.rb +11 -5
  37. data/lib/mobility/plugins/active_record.rb +3 -0
  38. data/lib/mobility/plugins/active_record/backend.rb +2 -0
  39. data/lib/mobility/plugins/active_record/query.rb +7 -7
  40. data/lib/mobility/plugins/active_record/uniqueness_validation.rb +5 -1
  41. data/lib/mobility/plugins/arel.rb +125 -0
  42. data/lib/mobility/plugins/arel/nodes.rb +15 -0
  43. data/lib/mobility/plugins/arel/nodes/pg_ops.rb +134 -0
  44. data/lib/mobility/plugins/attribute_methods.rb +1 -0
  45. data/lib/mobility/plugins/attributes.rb +17 -15
  46. data/lib/mobility/plugins/backend.rb +45 -22
  47. data/lib/mobility/plugins/cache.rb +12 -5
  48. data/lib/mobility/plugins/default.rb +1 -1
  49. data/lib/mobility/plugins/fallbacks.rb +4 -4
  50. data/lib/mobility/plugins/fallthrough_accessors.rb +5 -6
  51. data/lib/mobility/plugins/locale_accessors.rb +2 -5
  52. data/lib/mobility/plugins/presence.rb +1 -1
  53. data/lib/mobility/plugins/reader.rb +2 -2
  54. data/lib/mobility/plugins/sequel/dirty.rb +2 -2
  55. data/lib/mobility/plugins/writer.rb +1 -1
  56. data/lib/mobility/version.rb +2 -2
  57. data/lib/rails/generators/mobility/templates/create_string_translations.rb +0 -1
  58. data/lib/rails/generators/mobility/templates/create_text_translations.rb +0 -1
  59. data/lib/rails/generators/mobility/templates/initializer.rb +11 -3
  60. metadata +14 -20
  61. metadata.gz.sig +0 -0
  62. data/lib/mobility/active_record/model_translation.rb +0 -14
  63. data/lib/mobility/active_record/string_translation.rb +0 -10
  64. data/lib/mobility/active_record/text_translation.rb +0 -10
  65. data/lib/mobility/active_record/translation.rb +0 -14
  66. data/lib/mobility/arel.rb +0 -49
  67. data/lib/mobility/arel/nodes.rb +0 -13
  68. data/lib/mobility/arel/nodes/pg_ops.rb +0 -132
  69. data/lib/mobility/arel/visitor.rb +0 -61
  70. data/lib/mobility/sequel/column_changes.rb +0 -28
  71. data/lib/mobility/sequel/hash_initializer.rb +0 -21
  72. data/lib/mobility/sequel/model_translation.rb +0 -20
  73. data/lib/mobility/sequel/sql.rb +0 -16
  74. data/lib/mobility/sequel/string_translation.rb +0 -10
  75. data/lib/mobility/sequel/text_translation.rb +0 -10
  76. data/lib/mobility/sequel/translation.rb +0 -53
@@ -100,7 +100,7 @@ On top of this, a backend will normally:
100
100
  # @param [Symbol] locale Locale to read
101
101
  # @return [TrueClass,FalseClass] Whether translation is present for locale
102
102
  def present?(locale, options = {})
103
- Util.present?(read(locale, options))
103
+ Util.present?(read(locale, **options))
104
104
  end
105
105
 
106
106
  # @!method model_class
@@ -120,6 +120,13 @@ On top of this, a backend will normally:
120
120
 
121
121
  # Defines setup hooks for backend to customize model class.
122
122
  module ClassMethods
123
+ # Returns valid option keys for this backend. This is overriden in
124
+ # backends to define which keys are valid for each backend class.
125
+ # @return [Array]
126
+ def valid_keys
127
+ []
128
+ end
129
+
123
130
  # Assign block to be called on model class.
124
131
  # @yield [attribute_names, options]
125
132
  # @note When called multiple times, setup blocks will be appended
@@ -179,15 +186,6 @@ On top of this, a backend will normally:
179
186
  EOM
180
187
  end
181
188
 
182
- # Called from plugins to apply custom processing for this backend.
183
- # Name is the name of the plugin.
184
- # @param [Symbol] name Name of plugin
185
- # @return [Boolean] Whether the plugin was applied
186
- # @note This is currently only called by Plugins::Cache.
187
- def apply_plugin(_)
188
- false
189
- end
190
-
191
189
  # Show useful information about this backend class, if it has no name.
192
190
  # @return [String]
193
191
  def inspect
@@ -5,7 +5,7 @@ module Mobility
5
5
  class << self
6
6
  # @param [Symbol, Object] backend Name of backend to load.
7
7
  def load_backend(name)
8
- return name if Module === name
8
+ return name if Module === name || name.nil?
9
9
 
10
10
  unless (backend = @backends[name])
11
11
  require "mobility/backends/#{name}"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "mobility/backend"
3
- require "mobility/arel"
3
+ require "mobility/plugins/arel"
4
4
 
5
5
  module Mobility
6
6
  module Backends
@@ -57,7 +57,7 @@ can be run again to add new attributes or locales.)
57
57
  # on model table
58
58
  def self.build_node(attr, locale)
59
59
  model_class.arel_table[Column.column_name_for(attr, locale)]
60
- .extend(::Mobility::Arel::MobilityExpressions)
60
+ .extend(Plugins::Arel::MobilityExpressions)
61
61
  end
62
62
 
63
63
  private
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "mobility/backends/active_record"
3
- require "mobility/arel/nodes/pg_ops"
3
+ require "mobility/backends/container"
4
+ require "mobility/plugins/arel/nodes/pg_ops"
4
5
 
5
6
  module Mobility
6
7
  module Backends
@@ -11,11 +12,7 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
11
12
  =end
12
13
  class ActiveRecord::Container
13
14
  include ActiveRecord
14
-
15
- # @!method column_name
16
- # Returns name of json or jsonb column used to store translations
17
- # @return [Symbol] (:translations) Name of translations column
18
- option_reader :column_name
15
+ include Container
19
16
 
20
17
  # @!group Backend Accessors
21
18
  #
@@ -52,15 +49,15 @@ Implements the {Mobility::Backends::Container} backend for ActiveRecord models.
52
49
 
53
50
  # @param [String] attr Attribute name
54
51
  # @param [Symbol] locale Locale
55
- # @return [Mobility::Arel::Nodes::Json,Mobility::Arel::Nodes::Jsonb] Arel
52
+ # @return [Mobility::Plugins::Arel::Nodes::Json,Mobility::Arel::Nodes::Jsonb] Arel
56
53
  # node for attribute on json or jsonb column
57
54
  def build_node(attr, locale)
58
55
  column = model_class.arel_table[column_name]
59
56
  case column_type
60
57
  when :json
61
- Arel::Nodes::JsonContainer.new(column, build_quoted(locale), build_quoted(attr))
58
+ Plugins::Arel::Nodes::JsonContainer.new(column, build_quoted(locale), build_quoted(attr))
62
59
  when :jsonb
63
- Arel::Nodes::JsonbContainer.new(column, build_quoted(locale), build_quoted(attr))
60
+ Plugins::Arel::Nodes::JsonbContainer.new(column, build_quoted(locale), build_quoted(attr))
64
61
  end
65
62
  end
66
63
 
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -18,17 +18,17 @@ Implements the {Mobility::Backends::Hstore} backend for ActiveRecord models.
18
18
 
19
19
  # @!macro backend_writer
20
20
  def write(locale, value, options = {})
21
- super(locale, value && value.to_s, options)
21
+ super(locale, value && value.to_s, **options)
22
22
  end
23
23
  # @!endgroup
24
24
 
25
25
  # @param [String] attr Attribute name
26
26
  # @param [Symbol] locale Locale
27
- # @return [Mobility::Arel::Nodes::Hstore] Arel node for value of
27
+ # @return [Mobility::Plugins::Arel::Nodes::Hstore] Arel node for value of
28
28
  # attribute key on hstore column
29
29
  def self.build_node(attr, locale)
30
30
  column_name = column_affix % attr
31
- Arel::Nodes::Hstore.new(model_class.arel_table[column_name], build_quoted(locale))
31
+ Plugins::Arel::Nodes::Hstore.new(model_class.arel_table[column_name], build_quoted(locale))
32
32
  end
33
33
  end
34
34
  end
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -32,11 +32,11 @@ Implements the {Mobility::Backends::Json} backend for ActiveRecord models.
32
32
 
33
33
  # @param [String] attr Attribute name
34
34
  # @param [Symbol] locale Locale
35
- # @return [Mobility::Arel::Nodes::Json] Arel node for value of
35
+ # @return [Mobility::Plugins::Arel::Nodes::Json] Arel node for value of
36
36
  # attribute key on jsonb column
37
37
  def self.build_node(attr, locale)
38
38
  column_name = column_affix % attr
39
- Arel::Nodes::Json.new(model_class.arel_table[column_name], build_quoted(locale))
39
+ Plugins::Arel::Nodes::Json.new(model_class.arel_table[column_name], build_quoted(locale))
40
40
  end
41
41
  end
42
42
  end
@@ -1,5 +1,5 @@
1
1
  require 'mobility/backends/active_record/pg_hash'
2
- require 'mobility/arel/nodes/pg_ops'
2
+ require 'mobility/plugins/arel/nodes/pg_ops'
3
3
 
4
4
  module Mobility
5
5
  module Backends
@@ -32,11 +32,11 @@ Implements the {Mobility::Backends::Jsonb} backend for ActiveRecord models.
32
32
 
33
33
  # @param [String] attr Attribute name
34
34
  # @param [Symbol] locale Locale
35
- # @return [Mobility::Arel::Nodes::Jsonb] Arel node for value of
35
+ # @return [Mobility::Plugins::Arel::Nodes::Jsonb] Arel node for value of
36
36
  # attribute key on jsonb column
37
37
  def self.build_node(attr, locale)
38
38
  column_name = column_affix % attr
39
- Arel::Nodes::Jsonb.new(model_class.arel_table[column_name], build_quoted(locale))
39
+ Plugins::Arel::Nodes::Jsonb.new(model_class.arel_table[column_name], build_quoted(locale))
40
40
  end
41
41
  end
42
42
  end
@@ -1,8 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
  require "mobility/backends/active_record"
3
3
  require "mobility/backends/key_value"
4
- require "mobility/active_record/string_translation"
5
- require "mobility/active_record/text_translation"
6
4
 
7
5
  module Mobility
8
6
  module Backends
@@ -37,21 +35,21 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
37
35
  super
38
36
  if type = options[:type]
39
37
  options[:association_name] ||= :"#{options[:type]}_translations"
40
- options[:class_name] ||= Mobility::ActiveRecord.const_get("#{type.capitalize}Translation")
38
+ options[:class_name] ||= const_get("#{type.capitalize}Translation")
41
39
  end
42
40
  options[:table_alias_affix] = "#{model_class}_%s_#{options[:association_name]}"
43
41
  rescue NameError
44
- raise ArgumentError, "You must define a Mobility::ActiveRecord::#{type.capitalize}Translation class."
42
+ raise ArgumentError, "You must define a Mobility::Backends::ActiveRecord::KeyValue::#{type.capitalize}Translation class."
45
43
  end
46
44
  # @!endgroup
47
45
 
48
46
  # @param [String] attr Attribute name
49
47
  # @param [Symbol] _locale Locale
50
- # @return [Mobility::Arel::Attribute] Arel attribute for aliased
48
+ # @return [Mobility::Plugins::Arel::Attribute] Arel attribute for aliased
51
49
  # translation table value column
52
50
  def build_node(attr, locale)
53
51
  aliased_table = class_name.arel_table.alias(table_alias(attr, locale))
54
- Arel::Attribute.new(aliased_table, :value, locale, self, attribute_name: attr.to_sym)
52
+ Plugins::Arel::Attribute.new(aliased_table, :value, locale, self, attr.to_sym)
55
53
  end
56
54
 
57
55
  # Joins translations using either INNER/OUTER join appropriate to the query.
@@ -119,7 +117,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
119
117
  # The title predicate has a non-nil value, so we can use an INNER JOIN,
120
118
  # whereas we are searching for nil content, which requires an OUTER JOIN.
121
119
  #
122
- class Visitor < ::Mobility::Arel::Visitor
120
+ class Visitor < Plugins::Arel::Visitor
123
121
  private
124
122
 
125
123
  def visit_Arel_Nodes_Equality(object)
@@ -141,7 +139,7 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
141
139
  transform_values { OUTER_JOIN }
142
140
  end
143
141
 
144
- def visit_Mobility_Arel_Attribute(object)
142
+ def visit_Mobility_Plugins_Arel_Attribute(object)
145
143
  if object.backend_class == backend_class && object.locale == locale
146
144
  { object.attribute_name => OUTER_JOIN }
147
145
  end
@@ -194,8 +192,8 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
194
192
 
195
193
  # Returns translation for a given locale, or builds one if none is present.
196
194
  # @param [Symbol] locale
197
- # @return [Mobility::ActiveRecord::TextTranslation,Mobility::ActiveRecord::StringTranslation]
198
- def translation_for(locale, _options = {})
195
+ # @return [Mobility::Backends::ActiveRecord::KeyValue::TextTranslation,Mobility::Backends::ActiveRecord::KeyValue::StringTranslation]
196
+ def translation_for(locale, **)
199
197
  translation = translations.find { |t| t.key == attribute && t.locale == locale.to_s }
200
198
  translation ||= translations.build(locale: locale, key: attribute)
201
199
  translation
@@ -205,12 +203,30 @@ Implements the {Mobility::Backends::KeyValue} backend for ActiveRecord models.
205
203
  def self.included(model_class)
206
204
  model_class.after_destroy do
207
205
  [:string, :text].each do |type|
208
- Mobility::ActiveRecord.const_get("#{type.capitalize}Translation").
206
+ Mobility::Backends::ActiveRecord::KeyValue.const_get("#{type.capitalize}Translation").
209
207
  where(translatable: self).destroy_all
210
208
  end
211
209
  end
212
210
  end
213
211
  end
212
+
213
+ class Translation < ::ActiveRecord::Base
214
+ self.abstract_class = true
215
+
216
+ belongs_to :translatable, polymorphic: true, touch: true
217
+
218
+ validates :key, presence: true, uniqueness: { scope: [:translatable_id, :translatable_type, :locale], case_sensitive: true }
219
+ validates :translatable, presence: true
220
+ validates :locale, presence: true
221
+ end
222
+
223
+ class TextTranslation < Translation
224
+ self.table_name = "mobility_text_translations"
225
+ end
226
+
227
+ class StringTranslation < Translation
228
+ self.table_name = "mobility_string_translations"
229
+ end
214
230
  end
215
231
 
216
232
  register_backend(:active_record_key_value, ActiveRecord::KeyValue)
@@ -30,6 +30,10 @@ Implements {Mobility::Backends::Serialized} backend for ActiveRecord models.
30
30
  include ActiveRecord
31
31
  include HashValued
32
32
 
33
+ def self.valid_keys
34
+ super + [:format]
35
+ end
36
+
33
37
  # @!group Backend Configuration
34
38
  # @param (see Backends::Serialized.configure)
35
39
  # @option (see Backends::Serialized.configure)
@@ -1,7 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
  require "mobility/backends/active_record"
3
3
  require "mobility/backends/table"
4
- require "mobility/active_record/model_translation"
5
4
 
6
5
  module Mobility
7
6
  module Backends
@@ -116,10 +115,10 @@ columns to that table.
116
115
 
117
116
  # @param [String] attr Attribute name
118
117
  # @param [Symbol] _locale Locale
119
- # @return [Mobility::Arel::Attribute] Arel node for column on translation table
118
+ # @return [Mobility::Plugins::Arel::Attribute] Arel node for column on translation table
120
119
  def build_node(attr, locale)
121
120
  aliased_table = model_class.const_get(subclass_name).arel_table.alias(table_alias(locale))
122
- Arel::Attribute.new(aliased_table, attr, locale, self)
121
+ Plugins::Arel::Attribute.new(aliased_table, attr, locale, self)
123
122
  end
124
123
 
125
124
  # Joins translations using either INNER/OUTER join appropriate to the
@@ -190,7 +189,7 @@ columns to that table.
190
189
  # we need an OUTER JOIN. In the second case, one attribute is matched
191
190
  # against a non-nil value, so we can use an INNER JOIN.
192
191
  #
193
- class Visitor < Arel::Visitor
192
+ class Visitor < Plugins::Arel::Visitor
194
193
  private
195
194
 
196
195
  def visit_Arel_Nodes_Equality(object)
@@ -227,7 +226,7 @@ columns to that table.
227
226
  end
228
227
  end
229
228
 
230
- def visit_Mobility_Arel_Attribute(object)
229
+ def visit_Mobility_Plugins_Arel_Attribute(object)
231
230
  # We compare table names here to ensure that attributes defined on
232
231
  # different backends but the same table will correctly get an OUTER
233
232
  # join when required. Use options[:table_name] here since we don't
@@ -245,7 +244,7 @@ columns to that table.
245
244
  if self.const_defined?(subclass_name, false)
246
245
  const_get(subclass_name, false)
247
246
  else
248
- const_set(subclass_name, Class.new(Mobility::ActiveRecord::ModelTranslation))
247
+ const_set(subclass_name, Class.new(Translation))
249
248
  end
250
249
 
251
250
  translation_class.table_name = options[:table_name]
@@ -283,7 +282,7 @@ columns to that table.
283
282
 
284
283
  # Returns translation for a given locale, or builds one if none is present.
285
284
  # @param [Symbol] locale
286
- def translation_for(locale, _)
285
+ def translation_for(locale, **)
287
286
  translation = translations.in_locale(locale)
288
287
  translation ||= translations.build(locale: locale)
289
288
  translation
@@ -303,6 +302,12 @@ columns to that table.
303
302
  destroy(empty_translations) if empty_translations.any?
304
303
  end
305
304
  end
305
+
306
+ # Subclassed dynamically to generate translation class.
307
+ class Translation < ::ActiveRecord::Base
308
+ self.abstract_class = true
309
+ validates :locale, presence: true
310
+ end
306
311
  end
307
312
 
308
313
  register_backend(:active_record_table, ActiveRecord::Table)
@@ -19,8 +19,16 @@ stored).
19
19
 
20
20
  =end
21
21
  module Container
22
+ def self.included(backend_class)
23
+ backend_class.extend ClassMethods
24
+ backend_class.option_reader :column_name
25
+ end
26
+
27
+ module ClassMethods
28
+ def valid_keys
29
+ [:column_name]
30
+ end
31
+ end
22
32
  end
23
-
24
- register_backend(:container, Container)
25
33
  end
26
34
  end
@@ -36,6 +36,10 @@ Defines read and write methods that access the value at a key with value
36
36
  end
37
37
 
38
38
  module ClassMethods
39
+ def valid_keys
40
+ [:column_prefix, :column_suffix]
41
+ end
42
+
39
43
  def configure(options)
40
44
  options[:column_affix] = "#{options[:column_prefix]}%s#{options[:column_suffix]}"
41
45
  end
@@ -6,7 +6,7 @@ Stores translations as hash on Postgres jsonb column.
6
6
 
7
7
  ==Backend Options
8
8
 
9
- ===+prefix+ and +suffix+
9
+ ===+column_prefix+ and +column_suffix+
10
10
 
11
11
  Prefix and suffix to add to attribute name to generate jsonb column name.
12
12
 
@@ -54,13 +54,13 @@ other backends on model (otherwise one will overwrite the other).
54
54
 
55
55
  # @!group Backend Accessors
56
56
  # @!macro backend_reader
57
- def read(locale, options = {})
58
- translation_for(locale, options).value
57
+ def read(locale, **options)
58
+ translation_for(locale, **options).value
59
59
  end
60
60
 
61
61
  # @!macro backend_writer
62
- def write(locale, value, options = {})
63
- translation_for(locale, options).value = value
62
+ def write(locale, value, **options)
63
+ translation_for(locale, **options).value = value
64
64
  end
65
65
  # @!endgroup
66
66
 
@@ -83,6 +83,10 @@ other backends on model (otherwise one will overwrite the other).
83
83
  end
84
84
 
85
85
  module ClassMethods
86
+ def valid_keys
87
+ [:type, :association_name, :class_name]
88
+ end
89
+
86
90
  # @!group Backend Configuration
87
91
  # @option options [Symbol,String] type Column type to use
88
92
  # @option options [Symbol] association_name (:<type>_translations) Name
@@ -100,16 +104,9 @@ other backends on model (otherwise one will overwrite the other).
100
104
  end
101
105
  end
102
106
 
103
- # Apply custom processing for plugin
104
- # @param (see Backend::ClassMethods#apply_plugin)
105
- # @return (see Backend::ClassMethods#apply_plugin)
106
- def apply_plugin(name)
107
- if name == :cache
108
- include self::Cache
109
- true
110
- else
111
- super
112
- end
107
+ # Apply custom processing for cache plugin
108
+ def include_cache
109
+ include self::Cache
113
110
  end
114
111
 
115
112
  def table_alias(attr, locale)
@@ -123,7 +120,7 @@ other backends on model (otherwise one will overwrite the other).
123
120
  if cache.has_key?(locale)
124
121
  cache[locale]
125
122
  else
126
- cache[locale] = super(locale, options)
123
+ cache[locale] = super(locale, **options)
127
124
  end
128
125
  end
129
126