lib/clevic/field.rb in clevic-0.6.0 vs lib/clevic/field.rb in clevic-0.7.0
- old
+ new
@@ -6,17 +6,18 @@
This defines a field in the UI, and how it hooks up to a field in the DB.
=end
class Field
include QtFlags
- attr_accessor :attribute, :path, :label, :delegate, :class_name, :alignment, :format, :tooltip
- attr_writer :sample
+ attr_accessor :attribute, :path, :label, :delegate, :class_name
+ attr_accessor :alignment, :format, :tooltip, :path_block
+ attr_writer :sample, :read_only
# attribute is the symbol for the attribute on the model_class
def initialize( attribute, model_class, options )
# sanity checking
- unless model_class.has_attribute?( attribute )
+ unless model_class.has_attribute?( attribute ) or model_class.instance_methods.include?( attribute.to_s )
msg = <<EOF
#{attribute} not found in #{model_class.name}. Possibilities are:
#{model_class.attribute_names.join("\n")}
EOF
raise msg
@@ -25,13 +26,20 @@
# set values
@attribute = attribute
@model_class = model_class
options.each do |key,value|
- self.send( "#{key}=", value ) if respond_to?( key )
+ self.send( "#{key}=", value ) if respond_to?( "#{key}=" )
end
+ # TODO could convert everything to a block here, even paths
+ if options[:display].kind_of?( Proc )
+ @path_block = options[:display]
+ else
+ @path = options[:display]
+ end
+
# default the label
@label ||= attribute.to_s.humanize
# default formats
if @format.nil?
@@ -51,10 +59,35 @@
when :boolean; qt_aligncenter
end
end
end
+ # Return the attribute value for the given entity, which may
+ # be an ActiveRecord instance
+ # entity is an ActiveRecord instance
+ def value_for( entity )
+ return nil if entity.nil?
+ transform_attribute( entity.send( attribute ) )
+ end
+
+ # apply path, or path_block, to the given
+ # attribute value. Otherwise just return
+ # attribute_value itself
+ def transform_attribute( attribute_value )
+ return nil if attribute_value.nil?
+ case
+ when !path_block.nil?
+ path_block.call( attribute_value )
+
+ when !path.nil?
+ attribute_value.evaluate_path( path.split( /\./ ) )
+
+ else
+ attribute_value
+ end
+ end
+
# return true if it's a date, a time or a datetime
# cache result because the type won't change in the lifetime of the field
def is_date_time?
@is_date_time ||= [:time, :date, :datetime].include?( meta.type )
end
@@ -63,15 +96,23 @@
# in other words an ActiveRecord::ConnectionAdapters::Column object
def meta
@model_class.columns_hash[attribute.to_s] || @model_class.reflections[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 filtered on.
+ def filterable?
+ !meta.nil?
+ end
+
# return the name of the field for this Field, quoted for the dbms
def quoted_field
@model_class.connection.quote_column_name( meta.name )
end
+ # return the result of the attribute + the path
def column
[attribute.to_s, path].compact.join('.')
end
# return an array of the various attribute parts
@@ -79,10 +120,15 @@
pieces = [ attribute.to_s ]
pieces.concat( path.split( /\./ ) ) unless path.nil?
pieces.map{|x| x.to_sym}
end
+ # is the field read-only. Defaults to false.
+ def read_only?
+ @read_only || false
+ end
+
# format this value. Use strftime for date_time types, or % for everything else
def do_format( value )
if self.format != nil
if is_date_time?
value.strftime( format )
@@ -101,20 +147,23 @@
case meta.type
# max width of 40 chars
when :string, :text
string_sample( 'n'*40 )
- when :date, :time, :datetime
+ when :date, :time, :datetime, :timestamp
date_time_sample
when :numeric, :decimal, :integer, :float
numeric_sample
# TODO return a width, or something like that
when :boolean; 'W'
+ when ActiveRecord::Reflection::AssociationReflection
+ #TODO width for relations
+
else
- puts "#{@model_class.name}.#{attribute} is a #{meta.type}"
+ puts "#{@model_class.name}.#{attribute} is a #{meta.type.inspect}"
end
if $options[:debug]
puts "@sample for #{@model_class.name}.#{attribute} #{meta.type}: #{@sample.inspect}"
end