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