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(:accessor, ["title"], backend: :my_backend, locale_accessors: [:en, :ja], cache: true, fallbacks: true)

will generate an anonymous module looking something like this:

Module.new do
  def title_backend
    # Create a subclass of Mobility::Backend::MyBackend and include in it:
    # - Mobility::Cache (from the cache: true option)
    # - Mobility::Fallbacks (from the fallbacks: true option)
    # 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 then add the backend method, the reader, writer and presence methods, and the locale accessor so the model class.

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 Backend::KeyValue and Backend::Table backends.

The setup block is also used to extend the i18n scope/dataset 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(method, *_attributes, **_options) ⇒ Attributes

Returns a new instance of Attributes.

Parameters:

  • method (Symbol)

    One of: [reader, writer, accessor]

  • _attributes (Array<String>)

    Attributes to define backend for

  • _options (Hash)

    Backend options hash

Options Hash (**_options):

  • model_class (Class)

    Class of model

  • locale_accessors (Boolean, Array<Symbol>)

    Enable locale accessors or specify locales for which accessors should be defined on this model backend. Will default to true if dirty option is true.

  • cache (Boolean) — default: true

    Enable cache for this model backend

  • fallbacks (Boolean, Hash)

    Enable fallbacks or specify fallbacks for this model backend

  • dirty (Boolean)

    Enable dirty tracking for this model backend

Raises:

  • (ArgumentError)

    if method is not reader, writer or accessor



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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 125

def initialize(method, *_attributes, **_options)
  raise ArgumentError, "method must be one of: reader, writer, accessor" unless %i[reader writer accessor].include?(method)
  @options = _options
  @attributes = _attributes.map &:to_s
  model_class = options[:model_class]
  @backend_name = options.delete(:backend) || Mobility.config.default_backend
  @backend_class = Class.new(get_backend_class(backend:     @backend_name,
                                               model_class: model_class))

  options[:locale_accessors] ||= true if options[:dirty]

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

  @backend_class.include Backend::Cache unless options[:cache] == false
  @backend_class.include Backend::Dirty.for(model_class) if options[:dirty]
  @backend_class.include Backend::Fallbacks if options[:fallbacks]
  @accessor_locales = options[:locale_accessors]
  @accessor_locales = Mobility.config.default_accessor_locales if options[:locale_accessors] == true

  attributes.each do |attribute|
    define_backend(attribute)

    if %i[accessor reader].include?(method)
      define_method attribute do |**options|
        mobility_get(attribute, options)
      end

      define_method "#{attribute}?" do |**options|
        mobility_present?(attribute, options)
      end
    end

    define_method "#{attribute}=" do |value|
      mobility_set(attribute, value)
    end if %i[accessor writer].include?(method)

    define_locale_accessors(attribute, @accessor_locales) if @accessor_locales
  end
end

Instance Attribute Details

#attributesArray<String> (readonly)

Attributes for which accessors will be defined

Returns:

  • (Array<String>)

    Array of attributes



100
101
102
# File 'lib/mobility/attributes.rb', line 100

def attributes
  @attributes
end

#backend_classClass (readonly)

Backend class

Returns:

  • (Class)

    Backend class



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

def backend_class
  @backend_class
end

#backend_nameSymbol, Class (readonly)

Name of backend

Returns:

  • (Symbol, Class)

    Name of backend, or backend class



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

def backend_name
  @backend_name
end

#optionsHash (readonly)

Backend options

Returns:

  • (Hash)

    Backend options



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

def options
  @options
end

Instance Method Details

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

Yield each attribute to block

Yields:



175
176
177
# File 'lib/mobility/attributes.rb', line 175

def each &block
  attributes.each &block
end

#included(model_class) ⇒ Object

Add this attributes module to shared Wrapper and setup model with backend setup block (see Backend::Setup#setup_model).

Parameters:

  • model_class (Class)

    Class of model



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

def included(model_class)
  model_class.mobility << self
  backend_class.setup_model(model_class, attributes, options)
end