lib/clevic/field.rb in clevic-0.13.0.b5 vs lib/clevic/field.rb in clevic-0.13.0.b6
- old
+ new
@@ -5,16 +5,15 @@
require 'clevic/many_field.rb'
module Clevic
=begin rdoc
+
This defines a field in the UI, and how it hooks up to a field in the DB.
-Many attributes are DSL-style accessors, where the value can be
-set with either an assignment or by passing a parameter. Unfortunately
-rdoc seems to have lost the ability to display these nicely. Anyway, here's
-an example
+Some attributes are DSL-style accessors, where the value can be
+set with either an assignment or by passing a parameter. For example:
property :ixnay
will allow
@@ -34,29 +33,23 @@
Generally properties are for options that can be passed to the field creation
method in ModelBuilder, whereas ruby attributes are for the internal workings.
#--
-TODO decide whether value_for type methods take an entity and do_something methods
-take a value.
-
-TODO the xxx_for methods are in here because their return values don't change
-by entity. Well, maybe sometimes they do. Anyway, need to find a better location
-for these and a better caching strategy.
-
-TODO this class is a bit confused about whether it handles metadata or record data, or both.
-
-TODO meta needs to handle virtual fields better.
+Yes, the blank line before class Field is really necessary.
+And so it the #-- above.
=end
+
class Field
# For defining properties
include Gather
# for formatting values
include GenericFormat
##
+ # :attr:
# The value to be displayed after being optionally format-ed
#
# Takes a String, a Symbol, or a Proc.
#
# A String will be a dot-separated path of attributes starting on the object returned by attribute.
@@ -69,56 +62,65 @@
#
# Defaults to nil, in other words the value of the attribute for this field.
property :display
##
+ # :attr:
# The label to be displayed in the column headings. Defaults to the humanised field name.
property :label
##
+ # :attr:
# One of the alignment specifiers - :left, :centre, :right or :justified.
# Defaults to right for numeric fields, centre for boolean, and left for
# other values.
property :alignment
##
+ # :attr:
# something to do with the icon that Qt displays. Not implemented yet.
property :decoration
##
+ # :attr:
# This defines how to format the value returned by :display. It takes a string or a Proc.
# Generally the string is something
# that can be understood by strftime (for time and date fields) or understood
# by % (for everything else). It can also be a Proc that has one parameter -
# the current entity. There are sensible defaults for common field types.
property :format
##
+ # :attr:
# This is just like format, except that it's used to format the value just
# before it's edited. A good use of this is to display dates with a 2-digit year
# but edit them with a 4 digit year.
# Defaults to a sensible value for some fields, for others it will default to the value of :format.
property :edit_format
##
+ # :attr:
# Whether the field is currently visible or not.
property :visible
##
+ # :attr:
# Sample is used if the programmer wishes to provide a value (that will be converted
# using to_s) that can be used
# as the basis for calculating the width of the field. By default this will be
# calculated from the database, but this may be an expensive operation, and
# doesn't always work properly. So we
# have the option to override that if we wish.
property :sample
##
+ # :attr:
# Takes a boolean. Set the field to read-only.
property :read_only
##
+ # :attr:
# The foreground and background colors.
# Can take a Proc, a string, or a symbol.
# - A Proc is called with an entity
# - A String is treated as a constant which may be one of the string constants understood by Qt::Color
# - A symbol is treated as a method to be call on an entity
@@ -126,50 +128,57 @@
# The result can be a Qt::Color, or one of the strings in
# http://www.w3.org/TR/SVG/types.html#ColorKeywords.
property :foreground, :background
##
+ # :attr:
# Can take a Proc, a string, or a symbol.
# - A Proc is called with an entity
# - A String is treated as a constant
# - A symbol is treated as a method to be call on an entity
property :tooltip
##
+ # :attr:
# An Enumerable of allowed values for restricted fields. If each yields
# two values (like it does for a Hash), the
# first will be stored in the db, and the second displayed in the UI.
# If it's a proc, that must return an Enumerable as above.
property :set
##
+ # :attr:
# When this is true, only the values in the combo may be entered.
# Otherwise the text-entry part of the combo can be used to enter
# non-listed values. Default is true if a set is explicitly specified.
# Otherwise depends on the field type.
property :restricted
##
+ # :attr:
# Only for the distinct field type. The values will be sorted either with the
# most used values first (:frequency => true) or in
# alphabetical order (:description => true).
# FIXME re-implement this with Dataset
property :frequency, :description
##
+ # :attr:
# Default value for this field for new records.
# Can be a Proc or a value. A value will just be
# set, a proc will be executed with the entity as a parameter.
property :default
##
+ # :attr:
# The property used for finding the field, ie by TableModel#field_column.
# Defaults to the attribute. If there are several display fields based on
# one db field, their attribute will be the same, but their id must be different.
property :id
##
+ # :attr:
# Called when the data in this field changes.
# Either a proc( clevic_view, table_view, model_index ) or a symbol
# for a method( view, model_index ) on the Clevic::View object.
property :notify_data_changed
@@ -213,13 +222,13 @@
# The UI delegate class for the field. The delegate class knows how to create a UI
# for this field using whatever GUI toolkit is selected
attr_accessor :delegate
- # The attribute on the AR entity that forms the basis for this field.
+ # The attribute on the entity that forms the basis for this field.
# Accessing the returned attribute (using send, or the [] method on an entity)
- # will give a simple value, or another AR entity in the case of relational fields.
+ # will give a simple value, or another entity in the case of relational fields.
# In other words, this is *not* the same as the name of the field in the DB, which
# would normally have an _id suffix for relationships.
attr_accessor :attribute
# The Object Relational Model this field uses to get data from.
@@ -234,16 +243,20 @@
# sanity checking
unless attribute.is_a?( Symbol )
raise "attribute #{attribute.inspect} must be a symbol"
end
- unless ( entity_class.is_a?( Clevic.base_entity_class ) and entity_class.has_attribute?( attribute ) ) or entity_class.instance_methods.include?( attribute.to_s )
- msg = <<EOF
-#{attribute} not found in #{entity_class.name}. Possibilities are:
-#{entity_class.attribute_names.join("\n")}
+ unless entity_class.ancestors.include?( Clevic.base_entity_class )
+ raise "#{entity_class} is not a Clevic.base_entity_class: #{Clevic.base_entity_class}"
+ end
+
+ # TODO this comes down to method_defined, really
+ unless entity_class.has_attribute?( attribute ) or entity_class.method_defined?( attribute )
+ raise <<EOF
+#{attribute.inspect} not found in #{entity_class.name}. Possibilities are:
+#{entity_class.attribute_names.inspect}
EOF
- raise msg
end
# instance variables
@attribute = attribute
# default to attribute, can be overwritten later
@@ -301,11 +314,11 @@
# return true if this is a field for a related table, false otherwise.
def association?
meta.andand.association?
end
- # ModelColumn object
+ # Clevic::ModelColumn object
def meta
entity_class.meta[attribute]
end
# return true if this field can be used in a filter
@@ -318,18 +331,19 @@
# return the result of the attribute + the path
def column
[attribute.to_s, path].compact.join('.')
end
- # return the class object of a related class if this is a relational
- # field, otherwise nil
+ # Return the class object of a related class if this is a relational
+ # field, otherwise nil.
def related_class
return nil unless association? && entity_class.meta.has_key?( attribute )
@related_class ||= eval( entity_class.meta[attribute].class_name || attribute.to_s.classify )
end
# return an array of the various attribute parts
+ # TODO not used much. Deprecate and remove.
def attribute_path
pieces = [ attribute.to_s ]
pieces.concat( display.to_s.split( '.' ) ) unless display.is_a? Proc
pieces.map{|x| x.to_sym}
end
@@ -337,21 +351,24 @@
# Return true if the field is read-only. Defaults to false.
def read_only?
@read_only || false
end
- # Called by Clevic::Model to format the display value.
+ # Called by Clevic::FieldValuer (and others) to format the display value.
def do_format( value )
do_generic_format( format, value )
end
- # Called by Clevic::Model to format the edit value.
+ # Called by Clevic::FieldValuer to format the field to a string value
+ # that can be used for editing.
def do_edit_format( value )
do_generic_format( edit_format, value )
end
# Set or return a sample for the field which can be used to size the UI field widget.
+ # If this is called as an accessor, and there is no value yet, a Clevic::Sampler
+ # instance is created to compute a sample.
def sample( *args )
if !args.empty?
@sample = args.first
self
else
@@ -424,12 +441,16 @@
# assume its an Enumerable
set
end
end
+ def to_s
+ "#{entity_class}.#{id}"
+ end
+
def inspect
- "#<Clevic::Field id=#{id.inspect}>"
+ "#<Clevic::Field #{entity_class} id=#{id} attribute=#{attribute}>"
end
protected
# call the conversion_block with the value, or just return the
@@ -492,13 +513,14 @@
else :left
end
end
# try to find a sensible display method
+ # TODO this code shows up in the default UI builder as well.
def default_display!
candidates = %W{#{entity_class.name.downcase} name title username to_s}
@display ||= candidates.find do |m|
- related_class.column_names.include?( m ) || related_class.instance_methods.include?( m )
+ related_class.column_names.include?( m ) || related_class.method_defined?( m )
end || raise( "Can't find one of #{candidates.inspect} in #{related_class.name}" )
end
end