module Spree::Preferences::Preferable

Preferable allows defining preference accessor methods.

A class including Preferable must implement preferences which should return an object responding to .fetch(key), []=(key, val), and .delete(key).

It may also define a `#context_for_default` method. It should return an array with the arguments to be provided to a proc used as the `default:` keyword for a preference.

The generated writer method performs typecasting before assignment into the preferences object.

Examples:

# Spree::Base includes Preferable and defines preferences as a serialized
# column.
class Settings < Spree::Base
  preference :color,       :string,  default: 'red'
  preference :temperature, :integer, default: 21
end

s = Settings.new
s.preferred_color # => 'red'
s.preferred_temperature # => 21

s.preferred_color = 'blue'
s.preferred_color # => 'blue'

# Typecasting is performed on assignment
s.preferred_temperature = '24'
s.preferred_color # => 24

# Modifications have been made to the .preferences hash
s.preferences #=> {color: 'blue', temperature: 24}

# Save the changes. All handled by activerecord
s.save!

Each preference gets rendered as a form field in Solidus backend.

As not all supported preference types are representable as a form field, only some of them get rendered per default. Arrays and Hashes for instance are supported preference field types, but do not represent well as a form field.

Overwrite allowed_admin_form_preference_types in your class if you want to provide more fields. If you do so, you also need to provide a preference field partial that lives in:

app/views/spree/admin/shared/preference_fields/

Public Instance Methods

admin_form_preference_names() click to toggle source

Preference names representable as form fields in Solidus backend

Not all preferences are representable as a form field.

Arrays and Hashes for instance are supported preference field types, but do not represent well as a form field.

As these kind of preferences are mostly developer facing and not admin facing we should not render them.

Overwrite allowed_admin_form_preference_types in your class that includes Spree::Preferable if you want to provide more fields. If you do so, you also need to provide a preference field partial that lives in:

app/views/spree/admin/shared/preference_fields/

@return [Array]

# File lib/spree/preferences/preferable.rb, line 140
def admin_form_preference_names
  defined_preferences.keep_if do |type|
    preference_type(type).in? self.class.allowed_admin_form_preference_types
  end
end
default_preferences() click to toggle source

@return [Hash{Symbol => Object}] Default for all preferences defined on this class

# File lib/spree/preferences/preferable.rb, line 114
def default_preferences
  Hash[
    defined_preferences.map do |preference|
      [preference, preference_default(preference)]
    end
  ]
end
defined_preferences() click to toggle source

@return [Array<Symbol>] All preferences defined on this class

# File lib/spree/preferences/preferable.rb, line 109
def defined_preferences
  self.class.defined_preferences
end
get_preference(name) click to toggle source

Get a preference @param name [#to_sym] name of preference @return [Object] The value of preference name

# File lib/spree/preferences/preferable.rb, line 69
def get_preference(name)
  has_preference! name
  send self.class.preference_getter_method(name)
end
has_preference!(name) click to toggle source

Raises an exception if the name preference is not defined on this class @param name [#to_sym] name of preference

# File lib/spree/preferences/preferable.rb, line 98
def has_preference!(name)
  raise NoMethodError.new "#{name} preference not defined" unless has_preference? name
end
has_preference?(name) click to toggle source

@param name [#to_sym] name of preference @return [Boolean] if preference exists on this class

# File lib/spree/preferences/preferable.rb, line 104
def has_preference?(name)
  defined_preferences.include?(name.to_sym)
end
preference_default(name) click to toggle source

@param name [#to_sym] name of preference @return [Object] The default for preference name

# File lib/spree/preferences/preferable.rb, line 91
def preference_default(name)
  has_preference! name
  send self.class.preference_default_getter_method(name)
end
preference_type(name) click to toggle source

@param name [#to_sym] name of preference @return [Symbol] The type of preference name

# File lib/spree/preferences/preferable.rb, line 84
def preference_type(name)
  has_preference! name
  send self.class.preference_type_getter_method(name)
end
set_preference(name, value) click to toggle source

Set a preference @param name [#to_sym] name of preference @param value [Object] new value for preference name

# File lib/spree/preferences/preferable.rb, line 77
def set_preference(name, value)
  has_preference! name
  send self.class.preference_setter_method(name), value
end

Private Instance Methods

context_for_default() click to toggle source
# File lib/spree/preferences/preferable.rb, line 184
def context_for_default
  [].freeze
end
convert_preference_value(value, type, preference_encryptor = nil) click to toggle source
# File lib/spree/preferences/preferable.rb, line 148
def convert_preference_value(value, type, preference_encryptor = nil)
  return nil if value.nil?
  case type
  when :string, :text
    value.to_s
  when :encrypted_string
    preference_encryptor.encrypt(value.to_s)
  when :password
    value.to_s
  when :decimal
    begin
      value.to_s.to_d
    rescue ArgumentError
      BigDecimal(0)
    end
  when :integer
    value.to_i
  when :boolean
    if !value ||
       value.to_s =~ /\A(f|false|0|^)\Z/i ||
       (value.respond_to?(:empty?) && value.empty?)
      false
    else
      true
    end
  when :array
    raise TypeError, "Array expected got #{value.inspect}" unless value.is_a?(Array)
    value
  when :hash
    raise TypeError, "Hash expected got #{value.inspect}" unless value.is_a?(Hash)
    value
  else
    value
  end
end