lib/clevic/field.rb in clevic-0.13.0.b9 vs lib/clevic/field.rb in clevic-0.13.0.b10

- old
+ new

@@ -17,14 +17,14 @@ will allow # reader instance.ixnay - + # writer instance.ixnay = 'nix, baby' - + # writer instance.ixnay 'nix baby' # store the block for later instance.ixnay do |*args| @@ -40,14 +40,14 @@ =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. @@ -60,123 +60,123 @@ # A Proc will be passed the current entity. This can be used to display 'virtual' # fields from related tables, or calculated fields. # # 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 - + # 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 # - # The result can be a Qt::Color, or one of the strings in + # 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. @@ -187,25 +187,25 @@ # Called in configuration for a field that works with a relationship. # dataset.filter( :blah => 'etc' ).order( :interesting_field ) def dataset dataset_roller end - + # TODO Still getting the Builder/Built conflict def dataset_roller # related class if it's an association, entity_class otherwise @dataset_roller ||= DatasetRoller.new( ( association? ? related_class : entity_class ).dataset ) end - + # The list of properties for ActiveRecord options. # There are actually from ActiveRecord::Base.VALID_FIND_OPTIONS, but it's protected. # Each element becomes a property. # TODO deprecate these # TODO warn or raise if these are used together with a dataset call AR_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ] AR_FIND_OPTIONS.each{|x| property x} - + # Return a list of find options and their values, but only # if the values are not nil def find_options AR_FIND_OPTIONS.inject(Hash.new) do |ha,x| option_value = self.send(x) @@ -213,74 +213,74 @@ ha[x] = option_value end ha end end - + # The model object (eg TableModel) this field is part of. # Set to TableModel by ModelBuilder#build attr_accessor :model - + # 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 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 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. attr_reader :entity_class - + # Create a new Field object that displays the contents of a database field in # the UI using the given parameters. # - attribute is the symbol for the attribute on the entity_class. # - entity_class is the Object Relational Model which this Field talks to. # - options is a hash of writable attributes in Field, which can be any of the properties defined in this class. def initialize( attribute, entity_class, options, &block ) # sanity checking unless attribute.is_a?( Symbol ) raise "attribute #{attribute.inspect} must be a symbol" end - + 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 end - + # instance variables @attribute = attribute # default to attribute, can be overwritten later @id = attribute @entity_class = entity_class @visible = true - + # initialise @value_cache = {} - + # handle options gather( options, &block ) - + # set various sensible defaults. They're not lazy accessors because # they might stay nil, and we don't want to keep evaluating them. default_label! default_format! default_edit_format! default_alignment! default_display! if association? end - + # Return the attribute value for the given Object Relational Model instance, or nil # if entity is nil. Will call transform_attribute. def value_for( entity ) begin return nil if entity.nil? @@ -288,84 +288,84 @@ rescue Exception => e puts "error for #{entity}.#{entity.send( attribute ).inspect} in value_for: #{e.message}" puts e.backtrace end end - + # Apply the value of the display property to the given # attribute value. Otherwise just return the # attribute_value itself. def transform_attribute( attribute_value ) return nil if attribute_value.nil? case display when Proc display.call( attribute_value ) - + when String attribute_value.evaluate_path( display.split( '.' ) ) - + when Symbol attribute_value.send( display ) - + else attribute_value end end - + # return true if this is a field for a related table, false otherwise. def association? meta.andand.association? end - + # Clevic::ModelColumn object def meta entity_class.meta[attribute] || ModelColumn.new( attribute, {} ) end - + # return true if this field can be used in a filter # virtual fields (ie those that don't exist in this field's # table) can't be used to filter on. def filterable? !meta.nil? end - + # 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. 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 - + # Return true if the field is read-only. Defaults to false. def read_only? @read_only || false end - + # Called by Clevic::FieldValuer (and others) to format the display value. def do_format( value ) do_generic_format( format, value ) end - + # 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? @@ -387,36 +387,36 @@ end end @sample end end - + # Called by Clevic::TableModel to get the tooltip value def tooltip_for( entity ) cache_value_for( :tooltip, entity ) end # TODO Doesn't do anything useful yet. def decoration_for( entity ) nil end - + # Called by Clevic::TableModel to get the foreground color value def foreground_for( entity ) cache_value_for( :foreground, entity ) {|x| string_or_color(x)} end - + # Called by Clevic::TableModel to get the background color value def background_for( entity ) cache_value_for( :background, entity ) {|x| string_or_color(x)} end - + # called when a new entity object is created to set default values # specified by the default property. def set_default_for( entity ) begin - entity[attribute] = + entity[attribute] = case default when String default when Proc default.call( entity ) @@ -424,35 +424,35 @@ rescue Exception => e puts e.message puts e.backtrace end end - + # fetch the permitted set of values for a restricted field. def set_for( entity ) case set when Proc # the Proc should return an enumerable set.call( entity ) - + when Symbol entity.send( set ) - + else # assume its an Enumerable set end end - + def to_s "#{entity_class}.#{id}" end - + def inspect "#<Clevic::Field #{entity_class} id=#{id} attribute=#{attribute}>" end - + protected # call the conversion_block with the value, or just return the # value if conversion_block is nil def convert_or_identity( value, &conversion_block ) @@ -460,11 +460,11 @@ value else conversion_block.call( value ) end end - + # symbol is the property name to fetch a value for. # It can be a Proc, a symbol, or a value responding to to_s. # In all cases, conversion block will be called # conversion_block takes the value expected back from the property # and converts it to something that Qt will understand. Mostly @@ -476,14 +476,14 @@ when Proc; convert_or_identity( value.call( entity ), &conversion_block ) unless entity.nil? when Symbol; convert_or_identity( entity.send( value ), &conversion_block ) unless entity.nil? else; @value_cache[symbol] ||=convert_or_identity( value, &conversion_block ) end end - + # the label if it's not defined. Based on the attribute. def default_label! - @label ||= attribute.to_s.humanize + @label ||= ( id || attribute ).to_s.humanize end # sensible display format defaults if they're not defined. def default_format! @format ||= @@ -492,10 +492,10 @@ when :date; '%d-%h-%y' when :datetime; '%d-%h-%y %H:%M:%S' when :decimal, :float; "%.2f" end end - + # sensible edit format defaults if they're not defined. def default_edit_format! @edit_format ||= case meta.andand.type when :date; '%d-%h-%Y'