# coding: UTF-8
module Edgarj
module FormDrawer
# Edgarj::FormDrawer::Base draws HTML table tag based data entry form.
# This also provides default methods for SearchFormDrawer search condition
# entry form.
#
# === How to Customize
# 1. First, it may be enough to just redefine @options
# 1. Next, when draw_ATTR() is defined, it is called. See ModelPermissionControllerHelper for example.
# 1. Then, consider to overwrite draw_ATTR() method.
class Base
attr_accessor :vc, :f
# === INPUTS
# drawer:: Edgarj::Drawer instance
# record:: instance of AR
# f:: FormBuilder
def initialize(drawer, record, f)
@drawer = drawer
@vc = drawer.vc # just short-cut
@record = record
@f = f
# define default options for draw_X() method, where X is :flags, :kind,
# and so on. @options can be redefined at derived class.
@options = {
:flags => {},
:kind => {},
:boolean => {},
:default => {
:date => {:use_month_numbers=>true},
:integer => {:size=>16},
:text => {:size=>20},
}
}
end
# draw form
#
# I18n for label is:
#
# 1. usual column uses model.human_attribute_name()
# 1. 'belongs_to' column uses:
# ** I18n.t('activerecord.attributes.MODEL.EXT_ID')
# ** parent.human_name
def draw()
@vc.content_tag(:table) do
@left = true
@vc.capture do
for col in columns do
@vc.concat col.field(@record, self)
end
@vc.concat('
| '.html_safe) if !@left
end
end
end
def draw_field(rec, col)
draw_method = "draw_#{col.name}"
if self.class.method_defined?(draw_method) then
send(draw_method, col)
elsif (enum = get_enum(col))
draw_enum(col, enum)
elsif (bitset = get_bitset(col))
draw_bitset(col, bitset)
else
parent_model = @vc.model.belongs_to_AR(col)
if parent_model
_draw_belongs_to_field(parent_model, col)
else
_draw_field(col)
end
end
end
def columns
drawer = @vc.drawer
drawer.columns_for(drawer.form_columns, :form)
end
# base method for derived class
def edgarj_address?(col)
@record.class.edgarj_address?(col)
end
# base method for derived class
def edgarj_file?(col)
@record.class.edgarj_file?(col)
end
# return enum of the column
#
# Derived class must overwrite to return expected enum
def get_enum(col)
@vc.get_enum(@f.object.class, col)
end
# return bitset of the column
#
# Derived class must overwrite to return expected bitset
def get_bitset(col)
@vc.get_bitset(@f.object.class, col)
end
# flip field to left-lane or right-lane
def _draw_2_lane(&block)
result = @left ? @vc.tag(:tr, nil, true) : ''.html_safe
result += yield
result += ''.html_safe if !@left
@left = !@left # flip it
result
end
# draw head(label). SearchFormDrawer will overwrite to insert operator.
#
# === INPUTS
# col:: column info
# label:: if not-nil, label is used rather than human_attribute_name of col.name for field-label
# block:: wrapped field-drawing logic
#
def _draw_head(col, label=nil, &block)
_draw_2_lane{
html = @vc.content_tag(:th, label || @vc.column_label(col))
html << @vc.content_tag(:td, '')
html << (@vc.content_tag(:td) do yield end)
html
}
end
def draw_id(col)
''
end
def draw_type(col)
''
end
def draw_created_at(col)
''
end
def draw_updated_at(col)
''
end
# draw address fields
def draw_address(col)
_draw_head(col){ @vc.draw_address(@f, col) }
end
# draw bitset checkboxes field
def draw_bitset(col, bitset)
_draw_head(col){ @vc.draw_bitset(@f, col, bitset, @options[:flags]) }
end
# draw 'belongs_to' field for AR
def _draw_belongs_to_field(parent_model, col)
label = Settings.edgarj.belongs_to.popup_on == 'field' ?
nil :
@vc.draw_belongs_to_label(@f, @drawer.popup_path(col), col.name)
_draw_head(col, label){
@vc.draw_belongs_to_field(@f, @drawer.popup_path(col), col.name)
}
end
def draw_boolean(col)
_draw_head(col){ @vc.draw_boolean(@f, col, @options[:boolean]) }
end
def draw_file(col)
_draw_head(col){ @vc.draw_file(@f, col) }
end
def draw_enum(col, enum)
_draw_head(col){ @vc.draw_enum(@f, col, enum) }
end
# draw general field
def _draw_field(col)
case col.type
when :boolean
draw_boolean(col)
else
_draw_head(col){ @vc.draw_field(@f, col, @options[:default]) }
end
end
end
# Search-class differs from Base-class as follows:
# * draws the fields of ID, created_at, and updated_at for search
# * 'kind' selection has blank-option.
# * boolean is not checkbox. Rather, it is selection of [nil,false,true]
# * edgarj_file column is not drawn.
class Search < Base
# === INPUTS
# drawer:: Edgarj::Drawer instance
# model:: SearchForm object
# f:: FormBuilder object
# operator_builder:: FormBuilder object for operator
def initialize(drawer, model, f, operator_builder)
super(drawer, model, f)
@operator_builder = operator_builder
# merge options
@options[:kind].merge!(:include_blank=>true)
@options[:default][:date].merge!(:include_blank=>true)
end
# overwrite
def columns
drawer = @vc.drawer
drawer.columns_for(drawer.search_form_columns, :search_form)
end
# overwrite
def edgarj_address?(col)
@record._klass_str.constantize.edgarj_address?(col)
end
# overwrite
def edgarj_file?(col)
@record._klass_str.constantize.edgarj_file?(col)
end
# overwrite to insert operator
#
def _draw_head(col, label=nil, &block)
_draw_2_lane{
sprintf("%s | ", label || @vc.column_label(col)).html_safe +
# add operator for appropreate data type.
if col.name == 'id'
@vc.draw_search_operator(@operator_builder, '_id')
else
case col.type
when :date, :datetime, :integer
@vc.draw_search_operator(@operator_builder, col.name)
else
''
end
end + ' | '.html_safe + yield + ' | '.html_safe
}
end
# overwrite to draw 'id' search field
def draw_id(col)
_draw_head(col){ @f.text_field('_id', @options[:default][:integer]) }
end
# overwrite to draw created_at
def draw_created_at(col)
_draw_head(col){ @vc.draw_date(@f, col, @options[:default]) }
end
# overwrite to draw updated_at
def draw_updated_at(col)
_draw_head(col){ @vc.draw_date(@f, col, @options[:default]) }
end
# overwrite to draw address fields: TBD
def draw_address(col)
_draw_head(col){
'TBD'
}
end
# overwrite to draw 'belongs_to' field for SearchForm
#
# This is similar to Base class 'belongs_to field', but
# * add parent 'name' hidden field at drawing.
# * no link on parent name
def _draw_belongs_to_field(parent_model, col)
model = @f.object._klass_str.constantize
parent_name = @f.object._parent && @f.object._parent[col.name]
_draw_head(col, @vc.draw_belongs_to_label(@f, @drawer.popup_path_on_search(col), col.name, model)){
popup_field = Edgarj::PopupHelper::PopupField.new_builder(
@f.object_name, col.name)
@vc.content_tag(:span, parent_name, id: popup_field.label_target) +
@vc.hidden_field_tag(
'edgarj_search_form[search_form_parent][%s]' % col.name,
parent_name.blank? ? '' : parent_name,
id: popup_field.label_hidden_field) +
@vc.draw_belongs_to_clear_link(@f, col.name, popup_field,
parent_name,
'[' + @vc.draw_belongs_to_label_sub(model, col.name, parent_model) + ']')
}
end
# overwrite because it is totally different from checkbox on search.
def draw_boolean(col)
_draw_head(col){
@f.select(col.name, [
['(both)', ''],
['', 'false'],
['√', 'true']])
}
end
# overwrite to return enum of the column in SearchForm
#
# see overwritten draw_enum() also.
def get_enum(col)
@vc.get_enum(@record._klass_str.constantize, col)
end
# overwrite to add '(all)' at top of enum selection for search.
#
# see overwritten get_enum() also.
def draw_enum(col, enum)
_draw_head(col){
@vc.draw_enum(@f, col, enum,
:choice_1st => ['(all)',''],
:class => @record._klass_str.constantize
)
}
end
# overwrite to disable file upload column
def draw_file(col)
''
end
end
end
end