Skip to content

Introduction to Mobility v1.0

Chris Salzberg edited this page Dec 25, 2020 · 16 revisions

This page documents changes (mostly in configuration) between the latest 0.8 release and 1.0.

TL;DR

If you just want to update to 1.0 from an earlier version, update your Mobility.configure code to look like the one in the new initializer template.

Previously, inside the Mobility.configure block, you would call various configuration methods on the config. Instead, you now call plugins and in a block explicitly declare each plugin you want to use, along with any default (this replaces the default_options hash).

So suppose you had a configuration like this (in 0.8.x):

Mobility.configure do |config|
  config.default_backend = :key_value
  config.accessor_method = :translates
  config.query_method = :i18n
  config.default_options[:fallbacks] = true
  config.default_options[:type] = :text
end

It's important to realize here that default_options mixes plugin defaults and backend defaults. In 1.0, these are now separated, so you must pass any options related to the backend to the backend option in your declared plugins.

Also, Mobility previously quietly enabled the cache and presence plugins even if you did not explicitly ask for them. To get the same behaviour, you will need to specify them explicitly now.

In addition, Mobility v1.0 now breaks down many things that were previously combined. So when you enable the backend plugin, it only adds hooks and a mobility_backends method returning a hash of backends. To actually get reader and writer methods, you need to also include the reader and writer plugins. To also get methods returning the backend for a particular attribute (title_backend for the title backend), you need to also include the backend_reader plugin.

Finally, accessor_method is no longer supported (and you probably never used it, so safe to ignore).

So with that in mind, this configuration would be rewritten as follows:

Mobility.configure do
  plugins do
    backend :key_value, type: :text    # default_options[:type] is a backend option, so it must be passed to the `backend` plugin

    reader                             # Explicitly declare readers,
    writer                             # writers, and
    backend_reader                     # backend reader (post.title_backend, etc).

    active_record                      # You must now also explicitly ask for ActiveRecord (or Sequel)

    query                              # i18n is the default scope
    cache                              # previously implicit
    fallbacks
    presence                           # previously implicit
    default                            # previously implicit
    # attribute_methods                # uncomment this to get methods like `translated_attributes`
  end
end

If you get an exception about an invalid option key, there are two possible issues:

  • you are passing a key, say dirty: true, but you have not enabled the dirty plugin (just add dirty in the list above
  • you are passing a key for a backend, but the backend does not accept that key. A common case of this is passing type to a backend other than KeyValue, since no other backends define a type, and Mobility will now see this and complain.

Details of what changed

Configuration

Previously, Mobility had a Mobility::Configuration class with settings such as default_backend, accessor_method and query_method. These were global settings which could be accessed from any backend or plugin.

Similarly, while plugins existed, they were mostly implicitly included (fallbacks, dirty tracking, etc) and could only be "disabled" by passing a falsey value to translates, e.g. dirty: false. While this disabled them, the plugin was still present.

With 1.0, everything is centralized around plugins, none of which are included unless you ask for them. (This means that specs can also test conditions in complete isolation.)

So while the syntax looks similar, when you now call:

Mobility.configure do
  # ...

... the context in the block here is an instance of Mobility::Translations (previously Mobility::Attributes), with some methods to declare and customize plugins. You call plugins to declare plugins, with optional default settings.

Here is how we would set the default backend to :key_value and enable fallbacks and dirty tracking:

Mobility.configure do
  plugins do
    backend :key_value
    fallbacks
    dirty
  end
end

Plugins can have dependencies and those dependencies will be resolved inside the plugins block.

ORM plugin must be explicitly enabled

In the same spirit of making things more explicit, Mobility no longer inspects constants to figure out which ORM you're using; instead, you just declare the active_record or sequel plugin:

Mobility.configure do
  plugins do
    backend :key_value
    active_record # or sequel
    # ...
  end
end

Options are validated against plugins and backend options

Previously, you could pass any options to translates and if they were not used, they would be ignored. Now Mobility will complain if it sees a key it does not expect.

There are two contexts where this may happen, the first is when calling translates (or Mobility::Translations.new) and passing an option like dirty, but without actually enabling the dirty plugin. In this case, just add dirty to the list of plugins in Mobility.configure.

The second is if you pass an option to the backend in your configuration, but the backend does not accept that option. e.g.

Mobility.configure do
  plugins do
    backend :key_value, foo: 'bar'
  end
end

foo is not an option of the KeyValue backend, so Mobility will blow up with an exception. Just remove it. A common case of this is passing type to a backend other than the KeyValue backend.

Some (mostly internal) namespaces have changed

Previously, backends used namespaces like Mobility::ActiveRecord, Mobility::Arel and Mobility::Sequel. As of 1.0, the top namespace is kept very minimal with everything specific going under Mobility::Plugins and/or Mobility::Backends. Aside from keeping Mobility clean, it also avoids naming conflicts with other gems (see #258).

This may affect you if you are referring to constants like Mobility::ActiveRecord::TextTranslation or Mobility::ActiveRecord::StringTranslation (in the KeyValue backend), or similar classes in Sequel.

In general, if you get a NameErrror, the class name has probably changed to now be namespaced under Mobility::Backends, according to the backend that uses it:

  • Mobility::ActiveRecord::StringTranslation becomes Mobility::Backends::ActiveRecord::KeyValue::StringTranslation
  • Mobility::ActiveRecord::TextTranslation becomes Mobility::Backends::ActiveRecord::Table::TextTranslation
  • etc

For other details, see changes in #464.