Class: Mobility::Attributes

Inherits:
Module
  • Object
show all
Defined in:
lib/mobility/attributes.rb

Overview

Defines accessor methods to include on model class. Inspired by Traco's Traco::Attributes class.

Normally this class will be created through class methods defined using Translates accessor methods, and need not be created directly. However, the class is central to how Mobility hooks into models to add accessors and other methods, and should be useful as a reference when understanding and designing backends.

==Including Attributes in a Class

Since Attributes is a subclass of Module, including an instance of it is like including a module. Creating an instance like this:

Attributes.new("title", backend: :my_backend, locale_accessors: [:en, :ja], cache: true, fallbacks: true)

will generate an anonymous module that behaves like this:

Module.new do
def title_backend
  # Create a subclass of Mobility::Backends::MyBackend and include in it:
  # - Mobility::Plugins::Cache (from the +cache: true+ option)
  # - Mobility::Plugins::Fallbacks (from the +fallbacks: true+ option)
  # - Mobility::Plugins::Presence (by default, disabled by +presence: false+)
  # Then instantiate the backend, memoize it, and return it.
end

def title(**options)
  title_backend.read(Mobility.locale, **options).presence
end

def title?(**options)
  title_backend.read(Mobility.locale, **options).present?
end

def title=(value)
  title_backend.write(Mobility.locale, value.presence)
end

# Start Locale Accessors
#
def title_en(**options)
  title_backend.read(:en, **options).presence
end

def title_en?(**options)
  title_backend.read(:en, **options).present?
end

def title_en=(value)
  title_backend.write(:en, value.presence)
end

def title_ja(**options)
  title_backend.read(:ja, **options).presence
end

def title_ja?(**options)
  title_backend.read(:ja, **options).present?
end

def title_ja=(value)
  title_backend.write(:ja, value.presence)
end
# End Locale Accessors
end

Including this module into a model class will thus add the backend method, the reader, writer and presence methods, and the locale accessor so the model class. (These methods are in fact added to the model in an included hook.)

==Setting up the Model Class

Accessor methods alone are of limited use without a hook to actually modify the model class. This hook is provided by the Backend::Setup#setup_model method, which is added to every backend class when it includes the Backend module.

Assuming the backend has defined a setup block by calling setup, this block will be called when Attributes is #included in the model class, passed attributes and options defined when the backend was defined on the model class. This allows a backend to do things like (for example) define associations on a model class required by the backend, as happens in the Backends::KeyValue and Backends::Table backends.

The setup block is also used to extend the query scope/dataset (+i18n+ by default) with backend-specific query method support.

Since setup blocks are evaluated on the model class, it is possible that backends can conflict (for example, overwriting previously defined methods). Care should be taken to avoid defining methods on the model class, or where necessary, ensure that names are defined in such a way as to avoid conflicts with other backends.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*attribute_names, method: :accessor, backend: Mobility.default_backend, **backend_options) ⇒ Attributes

Returns a new instance of Attributes.

Parameters:

  • method (Symbol) (defaults to: :accessor)

    One of: [reader, writer, accessor]

  • attribute_names (Array<String>)

    Names of attributes to define backend for

  • backend_options (Hash)

    Backend options hash

Options Hash (**backend_options):

  • model_class (Class)

    Class of model

Raises:

  • (ArgumentError)

    if method is not reader, writer or accessor



131
132
133
134
135
136
137
138
# File 'lib/mobility/attributes.rb', line 131

def initialize(*attribute_names, method: :accessor, backend: Mobility.default_backend, **backend_options)
  raise ArgumentError, "method must be one of: reader, writer, accessor" unless %i[reader writer accessor].include?(method)
  @method = method
  @options = Mobility.default_options.merge(backend_options)
  @names = attribute_names.map(&:to_s)
  raise Mobility::BackendRequired, "Backend option required if Mobility.config.default_backend is not set." if backend.nil?
  @backend_name = backend
end

Instance Attribute Details

#backend_classClass (readonly)

Backend class

Returns:

  • (Class)

    Backend class



116
117
118
# File 'lib/mobility/attributes.rb', line 116

def backend_class
  @backend_class
end

#backend_nameSymbol, Class (readonly)

Name of backend

Returns:

  • (Symbol, Class)

    Name of backend, or backend class



120
121
122
# File 'lib/mobility/attributes.rb', line 120

def backend_name
  @backend_name
end

#methodSymbol (readonly)

Method (accessor, reader or writer)

Returns:

  • (Symbol)

    method



104
105
106
# File 'lib/mobility/attributes.rb', line 104

def method
  @method
end

#model_classClass (readonly)

Model class

Returns:

  • (Class)

    Class of model



124
125
126
# File 'lib/mobility/attributes.rb', line 124

def model_class
  @model_class
end

#namesArray<String> (readonly)

Attribute names for which accessors will be defined

Returns:

  • (Array<String>)

    Array of names



108
109
110
# File 'lib/mobility/attributes.rb', line 108

def names
  @names
end

#optionsHash (readonly)

Backend options

Returns:

  • (Hash)

    Backend options



112
113
114
# File 'lib/mobility/attributes.rb', line 112

def options
  @options
end

Instance Method Details

#each {|Attribute| ... } ⇒ Object

Yield each attribute name to block

Yield Parameters:

  • Attribute (String)


167
168
169
# File 'lib/mobility/attributes.rb', line 167

def each &block
  names.each(&block)
end

#included(klass) ⇒ Object

Setup backend class, include modules into model class, add this attributes module to shared Wrapper and setup model with backend setup block (see Backend::Setup#setup_model).

Parameters:

  • klass (Class)

    Class of model



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/mobility/attributes.rb', line 144

def included(klass)
  @model_class = @options[:model_class] = klass
  @backend_class = Class.new(get_backend_class(backend_name).for(model_class))

  @backend_class.configure(options) if @backend_class.respond_to?(:configure)

  Mobility.plugins.each do |name|
    plugin = get_plugin_class(name)
    plugin.apply(self, options[name])
  end

  names.each do |name|
    define_backend(name)
    define_reader(name) if %i[accessor reader].include?(method)
    define_writer(name) if %i[accessor writer].include?(method)
  end

  model_class.mobility << self
  backend_class.setup_model(model_class, names, options)
end