Class: Clevic::Field

Inherits:
Object show all
Includes:
GenericFormat, Gather
Defined in:
lib/clevic/field.rb,
lib/clevic/qt/field.rb,
lib/clevic/swing/field.rb

Overview

This defines a field in the UI, and how it hooks up to a field in the DB.

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

# reader
instance.ixnay

# writer
instance.ixnay = 'nix, baby'

# writer
instance.ixnay 'nix baby'

# store the block for later
instance.ixnay do |*args|
  # block stuff here
end

Generally properties are for options that can be passed to the field creation method in ModelBuilder, whereas ruby attributes are for the internal workings.

#– Yes, the blank line before class Field is really necessary. And so it the #– above.

Constant Summary collapse

AR_FIND_OPTIONS =

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

[ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ]

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from GenericFormat

#do_generic_format, #is_date_time?

Constructor Details

#initialize(attribute, entity_class, options, &block) ⇒ Field

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.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/clevic/field.rb', line 242

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

Instance Attribute Details

#attributeObject

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.



232
233
234
# File 'lib/clevic/field.rb', line 232

def attribute
  @attribute
end

#delegateObject

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



225
226
227
# File 'lib/clevic/field.rb', line 225

def delegate
  @delegate
end

#entity_classObject (readonly)

The Object Relational Model this field uses to get data from.



235
236
237
# File 'lib/clevic/field.rb', line 235

def entity_class
  @entity_class
end

#modelObject

The model object (eg TableModel) this field is part of. Set to TableModel by ModelBuilder#build



221
222
223
# File 'lib/clevic/field.rb', line 221

def model
  @model
end

Instance Method Details

#alignmentObject

: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.



76
# File 'lib/clevic/field.rb', line 76

property :alignment

#association?Boolean

return true if this is a field for a related table, false otherwise.

Returns:

  • (Boolean)


315
316
317
# File 'lib/clevic/field.rb', line 315

def association?
  meta.andand.association?
end

#attribute_pathObject

return an array of the various attribute parts TODO not used much. Deprecate and remove.



345
346
347
348
349
# File 'lib/clevic/field.rb', line 345

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

#background_for(entity) ⇒ Object

Called by Clevic::TableModel to get the background color value



409
410
411
# File 'lib/clevic/field.rb', line 409

def background_for( entity )
  cache_value_for( :background, entity ) {|x| string_or_color(x)}
end

#columnObject

return the result of the attribute + the path



332
333
334
# File 'lib/clevic/field.rb', line 332

def column
  [attribute.to_s, path].compact.join('.')
end

#datasetObject

This is the dataset of related objects. Called in configuration for a field that works with a relationship.

dataset.filter( :blah => 'etc' ).order( :interesting_field )


189
190
191
# File 'lib/clevic/field.rb', line 189

def dataset
  dataset_roller
end

#dataset_rollerObject

TODO Still getting the Builder/Built conflict



194
195
196
197
# File 'lib/clevic/field.rb', line 194

def dataset_roller
  # related class if it's an association, entity_class otherwise
  @dataset_roller ||= DatasetRoller.new( ( association? ? related_class : entity_class ).dataset )
end

#decorationObject

:attr: something to do with the icon that Qt displays. Not implemented yet.



81
# File 'lib/clevic/field.rb', line 81

property :decoration

#decoration_for(entity) ⇒ Object

TODO Doesn’t do anything useful yet.



399
400
401
# File 'lib/clevic/field.rb', line 399

def decoration_for( entity )
  nil
end

#defaultObject

: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.



169
# File 'lib/clevic/field.rb', line 169

property :default

#displayObject

: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. Paths longer than 1 element haven’t been tested much.

A Symbol refers to a method to be called on the current entity

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.



64
# File 'lib/clevic/field.rb', line 64

property :display

#do_edit_format(value) ⇒ Object

Called by Clevic::FieldValuer to format the field to a string value that can be used for editing.



363
364
365
# File 'lib/clevic/field.rb', line 363

def do_edit_format( value )
  do_generic_format( edit_format, value )
end

#do_format(value) ⇒ Object

Called by Clevic::FieldValuer (and others) to format the display value.



357
358
359
# File 'lib/clevic/field.rb', line 357

def do_format( value )
  do_generic_format( format, value )
end

#edit_formatObject

: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.



98
# File 'lib/clevic/field.rb', line 98

property :edit_format

#filterable?Boolean

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.

Returns:

  • (Boolean)


327
328
329
# File 'lib/clevic/field.rb', line 327

def filterable?
  !meta.nil?
end

#find_optionsObject

Return a list of find options and their values, but only if the values are not nil



209
210
211
212
213
214
215
216
217
# File 'lib/clevic/field.rb', line 209

def find_options
  AR_FIND_OPTIONS.inject(Hash.new) do |ha,x|
    option_value = self.send(x)
    unless option_value.nil?
      ha[x] = option_value
    end
    ha
  end
end

#foregroundObject

: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 www.w3.org/TR/SVG/types.html#ColorKeywords.



130
# File 'lib/clevic/field.rb', line 130

property :foreground, :background

#foreground_for(entity) ⇒ Object

Called by Clevic::TableModel to get the foreground color value



404
405
406
# File 'lib/clevic/field.rb', line 404

def foreground_for( entity )
  cache_value_for( :foreground, entity ) {|x| string_or_color(x)}
end

#formatObject

: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.



90
# File 'lib/clevic/field.rb', line 90

property :format

#frequencyObject

: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



162
# File 'lib/clevic/field.rb', line 162

property :frequency, :description

#idObject

: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.



176
# File 'lib/clevic/field.rb', line 176

property :id

#inspectObject



450
451
452
# File 'lib/clevic/field.rb', line 450

def inspect
  "#<Clevic::Field #{entity_class} id=#{id} attribute=#{attribute}>"
end

#labelObject

:attr: The label to be displayed in the column headings. Defaults to the humanised field name.



69
# File 'lib/clevic/field.rb', line 69

property :label

#metaObject

Clevic::ModelColumn object



320
321
322
# File 'lib/clevic/field.rb', line 320

def meta
  entity_class.meta[attribute] || ModelColumn.new( attribute, {} )
end

#notify_data_changedObject

: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.



183
# File 'lib/clevic/field.rb', line 183

property :notify_data_changed

#read_onlyObject

:attr: Takes a boolean. Set the field to read-only.



118
# File 'lib/clevic/field.rb', line 118

property :read_only

#read_only?Boolean

Return true if the field is read-only. Defaults to false.

Returns:

  • (Boolean)


352
353
354
# File 'lib/clevic/field.rb', line 352

def read_only?
  @read_only || false
end

Return the class object of a related class if this is a relational field, otherwise nil.



338
339
340
341
# File 'lib/clevic/field.rb', line 338

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

#restrictedObject

: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.



154
# File 'lib/clevic/field.rb', line 154

property :restricted

#sample(*args) ⇒ Object

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.



113
# File 'lib/clevic/field.rb', line 113

property :sample

#setObject

: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.



146
# File 'lib/clevic/field.rb', line 146

property :set

#set_default_for(entity) ⇒ Object

called when a new entity object is created to set default values specified by the default property.



415
416
417
418
419
420
421
422
423
424
425
426
427
428
# File 'lib/clevic/field.rb', line 415

def set_default_for( entity )
  begin
    entity[attribute] =
    case default
      when String
        default
      when Proc
        default.call( entity )
    end
  rescue Exception => e
    puts e.message
    puts e.backtrace
  end
end

#set_for(entity) ⇒ Object

fetch the permitted set of values for a restricted field.



431
432
433
434
435
436
437
438
439
440
441
442
443
444
# File 'lib/clevic/field.rb', line 431

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

#string_or_color(s_or_c) ⇒ Object

Convert a color name understood by java.awt.Color, or a 0xddccee style string to a java.awt.Color



6
7
8
9
10
11
12
13
14
15
# File 'lib/clevic/qt/field.rb', line 6

def string_or_color( s_or_c )
  case s_or_c
  when NilClass
    nil
  when Qt::Color
    s_or_c
  else
    Qt::Color.new( s_or_c.to_s )
  end
end

#swing_alignmentObject



25
26
27
28
29
30
31
32
# File 'lib/clevic/swing/field.rb', line 25

def swing_alignment
  case alignment
  when :left; javax.swing.SwingConstants::LEFT
  when :right; javax.swing.SwingConstants::RIGHT
  when :centre, :center; javax.swing.SwingConstants::CENTER
  else javax.swing.SwingConstants::LEADING
  end
end

#to_sObject



446
447
448
# File 'lib/clevic/field.rb', line 446

def to_s
  "#{entity_class}.#{id}"
end

#tooltipObject

: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



138
# File 'lib/clevic/field.rb', line 138

property :tooltip

#tooltip_for(entity) ⇒ Object

Called by Clevic::TableModel to get the tooltip value



394
395
396
# File 'lib/clevic/field.rb', line 394

def tooltip_for( entity )
  cache_value_for( :tooltip, entity )
end

#transform_attribute(attribute_value) ⇒ Object

Apply the value of the display property to the given attribute value. Otherwise just return the attribute_value itself.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/clevic/field.rb', line 297

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

#value_for(entity) ⇒ Object

Return the attribute value for the given Object Relational Model instance, or nil if entity is nil. Will call transform_attribute.



284
285
286
287
288
289
290
291
292
# File 'lib/clevic/field.rb', line 284

def value_for( entity )
  begin
    return nil if entity.nil?
    transform_attribute( entity.send( attribute ) )
  rescue Exception => e
    puts "error for #{entity}.#{entity.send( attribute ).inspect} in value_for: #{e.message}"
    puts e.backtrace
  end
end

#visibleObject

:attr: Whether the field is currently visible or not.



103
# File 'lib/clevic/field.rb', line 103

property :visible