module Anubis
module Etc
##
# Definitions of field options for table column or new (edit) form field.
class Field
# @!attribute [rw]
# Defines field title.
# @return [String] field's title
class_attribute :title, default: nil
# @!attribute [rw]
# Defines field type
#
# Possible values of field's type are 'string', 'integer', 'float', 'listbox', 'checkbox', 'longlistbox'
# and 'datetime'
# @return [String] field's type.
class_attribute :type, default: ''
# Defines date format for field type 'datetime'
# Possible values of field's format are 'date', 'full', 'datetime', 'month', 'year'.
# @return [String] field's type.
class_attribute :format, default: ''
# Defines precision for field type 'number'
# Possible values of this field is integer numbers between 0 and 6. If precision is 0 then number is integer.
# @return [String] field's type.
class_attribute :precision, default: 0
# @!attribute [rw]
# Defines field order options.
# @return [FieldOrder] field's order options
class_attribute :order, default: nil
# @!attribute [rw]
# Defines field's visibility for table representation data
# @return [Boolean] field's visibility
class_attribute :visible, default: true
# @!attribute [rw]
# Field's identifier
# @return [String] field's identifier
class_attribute :key, default: nil
# @!attribute [rw] field
# Field's name is used for access field value in model
# @return [String] field's name
class_attribute :field, default: nil
# @!attribute [rw] table_field
# Field's name is used for operation with table data (like 'where', 'order' and etc.)
# @return [String] field's name in the table
class_attribute :table_field, default: nil
# @!attribute [rw] error_text
# Text is shown when system can't access to data with presented {#field} name
# @return [String] field's error_text
class_attribute :error_text, default: ''
# @!attribute [rw]
# Describes additional field's options for type 'checkbox', 'listbox'.
# @return [FieldOptions] field's options.
class_attribute :options, default: nil
# @!attribute [rw]
# Describes if this field could return data for {Anubis::Data::Actions#autocomplete autocomplete} action.
# @return [Boolean] possibility for return data on autocomplete action.
#
# Options:
# - :limit (Integer) -- maximum number of elements (defaults to: 10)
# - :count (Integer) -- Minimum symbols count for output (defaults to: 3)
# @return [Hash] autocomplete definitions for field
class_attribute :autocomplete, default: false
# @!attribute [rw]
# Defines model's description for complex field
#
# Options:
# - :model (ActiveRecord) -- model class
# - :title (Symbol) -- field name is used for receive options titles (defaults to: :title)
# - :order (Symbol) -- field name is used for order options (defaults to: :title option)
# - :where (Hash) -- where parameters for select data from model (defaults to: {})
# @return [Model] model's description for complex field
class_attribute :model, default: nil
# @!attribute [rw] editable
# Defines if key of this field can be edited
# @return [String] returns path for edit field options
class_attribute :editable, default: nil
##
# Sets default parameters for field
# @param key [Symbol] field's identifier
# @param model [ActiveRecord] field's model
# @param options [Hash] field's initial options
def initialize(key, model, options = {})
#puts key
#puts options
self.field = nil
self.key = key.to_s
self.format = ''
self.precision = 0
self.title = options[:title] if options.key? :title
self.type = if options.key? :type then options[:type] else 'string' end
self.visible = if options.key? :visible then options[:visible] else true end
self.options = if options.key? :options then FieldOptions.new(options[:options]) else nil end
if options.key? :order
if options[:order].class == Hash
options[:order][:field] = Kernel.format('%s.%s', model.table_name, self.key.to_s) if !options[:order].key? :field
end
self.order = if options.key? :order then FieldOrder.new(options[:order]) else nil end
end
self.model = if options.key? :model then Model.new(options[:model]) else nil end
self.editable = if options.key? :editable then options[:editable] else nil end
self.autocomplete = if options.key? :autocomplete then options[:autocomplete] else nil end
self.error_text = if options.key? :error_text then options[:error_text] else I18n.t('errors.field_error') end
self.send Kernel.format('initialize_%s', self.type), options
if !options.key? :table_field
if !options.key? :field
if self.order
options[:table_field] = self.order.field if self.order.field
end
end
end
if !self.field
self.field = if options.key? :field then options[:field] else self.key end
end
self.table_field = if options.key?(:table_field) then options[:table_field] else Kernel.format('%s.%s', model.table_name, self.field) end
if self.autocomplete
self.autocomplete[:limit] = 10 if !autocomplete.key? :limit
self.autocomplete[:count] = 3 if !autocomplete.key? :count
self.autocomplete[:where] = []
end
#puts self.to_h
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'string' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_string (options)
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'boolean' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_boolean (options)
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'number' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_number (options)
if options.key? :error_text
self.error_text = options[:error_text]
else
self.error_text = ''
end
self.precision = options[:precision].to_s.to_i if options.key? :precision
self.precision = 0 if self.precision < 0
self.precision = 16 if self.precision > 16
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'text' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_text (options)
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'html' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_html (options)
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'datetime' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_datetime (options)
options[:format] = 'datetime' if !options.key? :format
options[:format] = 'datetime' if !%w[date datetime full year month].include? options[:format]
self.format = options[:format]
end
##
# Setups additional parameters for {Anubis::Etc::Field#type 'listbox' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_listbox (options)
self.options = FieldOptions.new if !self.options
if !self.options.list
if self.model
if !(%w[update create].include?(options[:action]))
self.options.show = 'update'
if self.options.line
self.options.list = self.options.line
else
self.options.list = {}
end
self.model.model.select(self.model.select).where(self.model.where).order(self.model.order).each do |dat|
self.options.list[dat.id.to_s.to_sym] = dat.send(self.model.title)
if dat.respond_to? :updated_at
if self.model.updated_at < dat.updated_at.to_time.utc.to_i
self.model.updated_at = dat.updated_at.to_time.utc.to_i
end
end
end
end
end
end
options[:format] = 'single' unless options.key? :format
options[:format] = 'single' unless %w[single multiple].include? options[:format]
self.format = options[:format]
end
##
# Initialize additional parameters for {Anubis::Etc::Field#type 'key' field type} for controller actions.
# @param options [Hash] field's initial options
def initialize_key (options)
if self.model
self.error_text = '' if options[:action] == 'new'
self.field = Kernel.format('%s.%s', self.key, self.model.title) if !options.key? :field
self.table_field = Kernel.format('%s.%s', self.model.model.table_name, self.model.title) if !self.table_field
self.autocomplete = {} if !options.key? :autocomplete
end
end
##
# Return field properties for frontend application
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @return [Hash] field's properties for defined action
def properties (model, action)
if %w[new edit].include? action
return self.properties_forms model, action
end
self.properties_index model, action
end
##
# Return field properties for frontend application for action 'index'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @return [Hash] field's properties for defined action
def properties_index (model, action)
result = {
id: self.key,
type: self.type,
sortable: self.order != nil
}
if self.title
result[:title] = self.title
else
result[:title] = model.human_attribute_name(self.key)
end
result[:editable] = self.editable if self.editable != nil
result[:editable] = false if self.type == 'key'
result[:format] = self.format if self.type == 'datetime'
result[:precision] = self.precision if self.type == 'number'
result[:options] = self.hash_to_json(self.options.list) if self.options
result
end
##
# Return field properties for frontend application for actions 'edit', 'new'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @return [Hash] field's properties for defined action
def properties_forms (model, action)
mod = model.new
result = {
id: self.key,
title: model.human_attribute_name(self.key),
type: self.type
}
result.merge!(self.send(Kernel.format('properties_forms_%s', self.type), model, action, mod))
result
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'string'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_string (model, action, mod)
result = {}
errors = {}
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::LengthValidator) }
if res
if res.options.key? :minimum
result[:min] = res.options[:minimum]
errors[:min] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :too_short, { count: result[:min] })
end
if res.options.key? :maximum
result[:max] = res.options[:maximum]
errors[:max] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :too_long, { count: result[:max] })
end
end
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
if res
result[:required] = true
errors[:required] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :blank)
end
result[:errors] = errors if errors.length > 0
result
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'number'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_number (model, action, mod)
result = {}
errors = {}
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::NumericalityValidator) }
if res
if res.options.key? :greater_than_or_equal_to
result[:min] = res.options[:greater_than_or_equal_to]
errors[:min] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :greater_than_or_equal_to, { count: result[:min] })
end
if res.options.key? :maximum
result[:max] = res.options[:maximum]
errors[:max] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :too_long, { count: result[:max] })
end
end
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
if res
result[:required] = true
errors[:required] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :blank)
end
result[:errors] = errors if errors.length > 0
result
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'text'.
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_text (model, action, mod)
self.properties_forms_string model, action, mod
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'html'.
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_html (model, action, mod)
self.properties_forms_text model, action, mod
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'listbox'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_listbox (model, action, mod)
result = {}
result[:format] = self.format if self.format == 'multiple'
result[:options] = self.hash_to_json(self.options.list) if self.options
result
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'key'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_key (model, action, mod)
result = {
type: 'string',
autocomplete: true,
editable: self.editable
}
errors = {}
result[:field] = self.model.title if self.editable
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
if res
result[:required] = true
errors[:required] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :blank)
end
result[:errors] = errors if errors.length > 0
result
end
##
# Return field properties for frontend application for actions 'edit', 'new' and type 'datetime'
# @param model [ActiveRecord] field's model
# @param action [Srting] current field action
# @param mod [ActiveRecord] initialized new model element
# @return [Hash] field's properties for defined action
def properties_forms_datetime (model, action, mod)
result = {}
errors = {}
res = model.validators_on(self.key.to_sym).detect { |v| v.is_a?(ActiveModel::Validations::PresenceValidator) }
if res
result[:required] = true
errors[:required] = model.human_attribute_name(self.key) + ' ' + mod.errors.generate_message(self.key.to_sym, :blank)
end
result[:format] = self.format
result[:errors] = errors if errors.length > 0
result
end
##
# Generates hash representation of all class parameters,
# @return [Hash] hash representation of all data
def to_h
result = {
key: self.key,
type: self.type,
visible: self.visible,
field: self.field,
table_field: self.table_field,
error_text: self.error_text,
autocomplete: self.autocomplete
}
result[:format] = self.format if self.type == 'datetime'
if self.editable
result[:editable] = self.editable
result[:field] = self.model.title if self.model
else
result[:editable] = self.editable if self.editable != nil
end
if self.model
result[:model] = self.model.to_h
end
if self.options
result[:options] = self.options.to_h
end
if self.order
result[:order] = self.order.to_h
end
result
end
##
# Convert hash to array json output
# @param hash [Hash] hash representation
# @return [Array] array representation
def hash_to_json(hash)
result = []
hash.each_key do |key|
result.push({ key: key.to_s, value: hash[key] })
end
result
end
public :format
end
end
end