lib/clevic/sampler.rb in clevic-0.13.0.b3 vs lib/clevic/sampler.rb in clevic-0.13.0.b5
- old
+ new
@@ -1,80 +1,126 @@
require 'clevic/generic_format.rb'
require 'andand'
module Clevic
-# Calculate a string sample for a particular Field
+# This is used as part of the process of calculating the width
+# of a field in the UI. Since the font is important, this computes
+# a string value for a field that can be given to the font metrics
+# for a framework. Uses various heuristics to compute the string
+# values for different kinds of fields.
class Sampler
- def initialize( entity_class, field_name, display, &block )
- @entity_class = entity_class
- @field_name = field_name
- @display = display
- @format_block = block
+ # display is only used for relational fields
+ def initialize( field, &format_block )
+ @field = field
+ @format_block = format_block
end
-
- attr_reader :entity_class, :field_name, :display
-
+ attr_reader :field
+
+ def entity_class
+ field.entity_class
+ end
+
+ def field_name
+ field.attribute
+ end
+
+ def display
+ field.display
+ end
+
def meta
- @meta ||= entity_class.meta[field_name]
+ field.meta
end
-
+
# return a string which is representative of the width of the field
def compute
- case meta.type
- when :string, :text
- string_sample
-
- 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 :many_to_one
- related_sample
-
+ if field.set
+ # choose the longest value in the set
+ set = field.set_for( entity_class.first )
+ if set.is_a?( Hash )
+ set.values
+ else
+ set
+ end. \
+ max{|a,b| a.to_s.length <=> b.to_s.length }.upcase
else
- if meta.type != NilClass
- raise "Sampler#compute can't figure out sample for #{entity_class.name}.#{field_name} because it's a #{meta.type.inspect}"
+ # choose samples based on the type of the field
+ case meta.type
+ when :boolean
+ field.label
+
+ when :string, :text
+ string_sample
+
+ 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 :many_to_one
+ related_sample
+
+ else
+ if meta.type != NilClass
+ raise "Sampler#compute can't figure out sample for #{entity_class.name}.#{field_name} because it's a #{meta.type.inspect}"
+ end
end
-
end
end
-
+
def do_format( value )
@format_block.call( value )
end
-
+
# default to max length of 20
def string_sample
'N' * ( entity_class.max( :length.sql_function( field_name ) ).andand.to_i || 20 )
end
- def date_time_sample
- sample_date = entity_class \
+ def sample_date_time
+ ds = entity_class \
.filter( ~{ field_name => nil } ) \
.select( field_name ) \
- .limit(1) \
- .single_value
- ;
+ .limit(1)
+ # can't use single-value here because the typecast_on_load
+ # isn't called unless we access the value via the entity object
+ ds.first.send( field_name )
+ end
+
+ def date_time_sample
# replace all letters with 'N'
- do_format( sample_date || Date.today ).gsub( /[[:alpha:]]/, 'N' )
+ # and numbers with 8
+ do_format( sample_date_time || Date.today ).andand.gsub( /[[:alpha:]]/, 'N' ).gsub( /\d/, '8' )
end
def numeric_sample
max = entity_class.max( field_name )
min = entity_class.min( field_name )
max_length = [ do_format( min ).to_s, do_format( max ).to_s ].map( &:length ).max
'9' * ( max_length || 5 )
end
+ # Hmm. The first reified exemplar of a relational nested Field
+ class VirtualField
+ def initialize( entity_class, display )
+ @entity_class, @display = entity_class, display
+ end
+
+ def entity_class; @entity_class; end
+ def attribute; @display; end
+ def display; nil; end
+ def meta; @entity_class.meta[attribute]; end
+ def set; nil; end
+ end
+
def related_sample
if display.respond_to?( :to_sym )
- Sampler.new( eval( meta.class_name ), display.to_sym, nil, &@format_block ).compute
+ Sampler.new( VirtualField.new( eval( meta.class_name ), display.to_sym ), &@format_block ).compute
end
end
end
end