clevic 0.13.0.b3 → 0.13.0.b5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +21 -0
- data/Manifest.txt +91 -85
- data/README.txt +33 -18
- data/Rakefile +2 -3
- data/TODO +8 -14
- data/bin/clevic +18 -20
- data/lib/clevic.rb +7 -1
- data/lib/clevic/action_builder.rb +4 -1
- data/lib/clevic/ar_methods.rb +72 -57
- data/lib/clevic/attribute_list.rb +4 -0
- data/lib/clevic/cache_table.rb +43 -69
- data/lib/clevic/dataset_roller.rb +22 -0
- data/lib/clevic/delegate.rb +11 -5
- data/lib/clevic/delegates/combo_delegate.rb +156 -0
- data/lib/clevic/delegates/distinct_delegate.rb +48 -0
- data/lib/clevic/delegates/relational_delegate.rb +59 -0
- data/lib/clevic/delegates/set_delegate.rb +31 -0
- data/lib/clevic/field.rb +39 -55
- data/lib/clevic/field_valuer.rb +23 -10
- data/lib/clevic/filter_command.rb +22 -36
- data/lib/clevic/framework.rb +37 -0
- data/lib/clevic/generic_format.rb +5 -1
- data/lib/clevic/many_field.rb +28 -3
- data/lib/clevic/model_builder.rb +27 -32
- data/lib/clevic/ordered_dataset.rb +45 -0
- data/lib/clevic/qt.rb +4 -1
- data/lib/clevic/qt/action_builder.rb +9 -1
- data/lib/clevic/qt/browser.rb +1 -1
- data/lib/clevic/qt/clipboard.rb +3 -3
- data/lib/clevic/qt/combo_delegate.rb +25 -89
- data/lib/clevic/qt/delegate.rb +25 -0
- data/lib/clevic/qt/distinct_delegate.rb +5 -23
- data/lib/clevic/qt/extensions.rb +8 -1
- data/lib/clevic/qt/qt_combo_box.rb +58 -0
- data/lib/clevic/qt/relational_delegate.rb +18 -58
- data/lib/clevic/qt/set_delegate.rb +4 -34
- data/lib/clevic/qt/simplest_delegate.rb +19 -0
- data/lib/clevic/qt/table_model.rb +10 -10
- data/lib/clevic/qt/table_view.rb +7 -23
- data/lib/clevic/qt/text_delegate.rb +2 -2
- data/lib/clevic/qt/ui/browser_ui.rb +1 -1
- data/lib/clevic/qt/ui/search_dialog_ui.rb +1 -1
- data/lib/clevic/rails_models_loaders.rb +13 -0
- data/lib/clevic/record.rb +2 -2
- data/lib/clevic/sampler.rb +85 -39
- data/lib/clevic/sequel_ar_adapter.rb +1 -28
- data/lib/clevic/sequel_clevic.rb +68 -0
- data/lib/clevic/sequel_meta.rb +1 -13
- data/lib/clevic/subclasses.rb +18 -0
- data/lib/clevic/swing.rb +2 -1
- data/lib/clevic/swing/action.rb +27 -3
- data/lib/clevic/swing/action_builder.rb +0 -2
- data/lib/clevic/swing/browser.rb +1 -10
- data/lib/clevic/swing/combo_delegate.rb +45 -133
- data/lib/clevic/swing/delegate.rb +2 -0
- data/lib/clevic/swing/distinct_delegate.rb +2 -14
- data/lib/clevic/swing/relational_delegate.rb +2 -20
- data/lib/clevic/swing/set_delegate.rb +13 -28
- data/lib/clevic/swing/table_view.rb +1 -1
- data/lib/clevic/table_model.rb +3 -4
- data/lib/clevic/table_searcher.rb +10 -31
- data/lib/clevic/table_view.rb +97 -43
- data/lib/clevic/ui/browser_ui.rb +133 -0
- data/lib/clevic/ui/search_dialog_ui.rb +106 -0
- data/lib/clevic/version.rb +2 -2
- data/models/accounts_models.rb +24 -21
- data/models/times_models.rb +34 -28
- data/models/times_psql_models.rb +9 -3
- data/models/times_sqlite_models.rb +24 -1
- data/sql/times_sqlite.sql +3 -3
- data/tasks/clevic.rake +2 -2
- data/test/test_cache_table.rb +9 -19
- data/test/test_table_searcher.rb +2 -5
- metadata +95 -91
- data/lib/clevic/order_attribute.rb +0 -63
- data/lib/clevic/qt/boolean_delegate.rb +0 -8
- data/lib/clevic/qt/delegates.rb +0 -1
- data/lib/clevic/qt/item_delegate.rb +0 -66
- data/lib/clevic/sql_dialects.rb +0 -33
- data/tasks/website.rake +0 -25
- data/test/test_order_attribute.rb +0 -62
- data/test/test_sql_dialects.rb +0 -77
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'clevic/delegates/combo_delegate.rb'
|
2
|
+
|
3
|
+
module Clevic
|
4
|
+
|
5
|
+
# Provide a list of all values in this field,
|
6
|
+
# and allow new values to be entered.
|
7
|
+
# :frequency can be set as an option. Boolean. If it's true
|
8
|
+
# the options are sorted in order of most frequently used first.
|
9
|
+
class DistinctDelegate
|
10
|
+
|
11
|
+
def needs_combo?
|
12
|
+
# works except when there is a null in the column
|
13
|
+
dataset.count > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO move away from ar_methods. Partly done.
|
17
|
+
# TODO ordering by either recentness, or frequency. OR both.
|
18
|
+
# TODO make sure nil is in the list. And the current item is at the top.
|
19
|
+
# TODO and the current item is in the list, even if it's older
|
20
|
+
# we only use the first column, so use the second
|
21
|
+
# column to sort by, since SQL requires the order by clause
|
22
|
+
# to be in the select list where distinct is involved
|
23
|
+
def dataset
|
24
|
+
base_dataset =
|
25
|
+
unless field.find_options.empty?
|
26
|
+
puts "conditions and order are deprecated. Use dataset instead."
|
27
|
+
require 'clevic/ar_methods'
|
28
|
+
field.entity_class.plugin :ar_methods
|
29
|
+
field.entity_class.translate( field.find_options )
|
30
|
+
else
|
31
|
+
field.dataset_roller.dataset
|
32
|
+
end
|
33
|
+
|
34
|
+
# now pull out the field and the distinct values
|
35
|
+
base_dataset. \
|
36
|
+
distinct. \
|
37
|
+
select( field.attribute ). \
|
38
|
+
order( field.attribute ). \
|
39
|
+
naked
|
40
|
+
end
|
41
|
+
|
42
|
+
def population
|
43
|
+
dataset.map( field.attribute )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'clevic/delegates/combo_delegate'
|
2
|
+
require 'clevic/dataset_roller.rb'
|
3
|
+
|
4
|
+
module Clevic
|
5
|
+
|
6
|
+
# Display a collection of possible related entities in the combo box.
|
7
|
+
# TODO this should be a module
|
8
|
+
class RelationalDelegate
|
9
|
+
def needs_combo?
|
10
|
+
dataset.count > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def empty_set_message
|
14
|
+
"There must be records in #{field.related_class} for this field to be editable."
|
15
|
+
end
|
16
|
+
|
17
|
+
def population
|
18
|
+
# dataset contains the set of all possible related entities,
|
19
|
+
|
20
|
+
# dataset is defined in Delegate
|
21
|
+
# entity is set in init_component
|
22
|
+
# field and entity are used by FieldValuer
|
23
|
+
|
24
|
+
# including the current entity.
|
25
|
+
# Could also use
|
26
|
+
# dataset.or( entity_class.primary_key => entity_key.pk )
|
27
|
+
# but that would put current entity in the list somewhere
|
28
|
+
# other than the top, which seems to be the most sensible
|
29
|
+
# place for it. Could also create a special enumerator
|
30
|
+
# which gives back the entity first, followed by the dataset.
|
31
|
+
dataset.all.with do |values|
|
32
|
+
# make sure there's only one instance of the current value,
|
33
|
+
# and make sure it's at the top of the list
|
34
|
+
values.delete( attribute_value )
|
35
|
+
values.unshift( attribute_value )
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# don't allow new values
|
40
|
+
def restricted?; true; end
|
41
|
+
|
42
|
+
protected
|
43
|
+
# Return an instance of the ORM dataset,
|
44
|
+
# right now that's Sequel::Dataset.
|
45
|
+
# This exists because convincing this functionality to
|
46
|
+
# coexist in the same method as dataset would be tricky.
|
47
|
+
def dataset
|
48
|
+
unless field.find_options.empty?
|
49
|
+
require 'clevic/ar_methods'
|
50
|
+
field.related_class.plugin :ar_methods
|
51
|
+
field.related_class.translate( field.find_options )
|
52
|
+
else
|
53
|
+
field.dataset_roller.dataset
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'clevic/delegates/combo_delegate.rb'
|
2
|
+
|
3
|
+
module Clevic
|
4
|
+
|
5
|
+
# A Combo box which allows a set of values. May or may not
|
6
|
+
# be restricted to the set.
|
7
|
+
# TODO this should be a module
|
8
|
+
class SetDelegate
|
9
|
+
# options must contain a :set => [ ... ] to specify the set of values.
|
10
|
+
def initialize( field )
|
11
|
+
raise "SetDelegate must have a :set in options" if field.set.nil?
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def needs_combo?
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def restricted?
|
20
|
+
field.restricted || false
|
21
|
+
end
|
22
|
+
|
23
|
+
# Items here could either be single values,
|
24
|
+
# or two-value arrays (from a hash-like set), so use key as db value
|
25
|
+
# and value as display value
|
26
|
+
def population
|
27
|
+
field.set_for( entity )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/clevic/field.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'gather'
|
2
2
|
require 'clevic/sampler.rb'
|
3
3
|
require 'clevic/generic_format.rb'
|
4
|
+
require 'clevic/dataset_roller.rb'
|
5
|
+
require 'clevic/many_field.rb'
|
4
6
|
|
5
7
|
module Clevic
|
6
8
|
|
@@ -136,7 +138,7 @@ class Field
|
|
136
138
|
# An Enumerable of allowed values for restricted fields. If each yields
|
137
139
|
# two values (like it does for a Hash), the
|
138
140
|
# first will be stored in the db, and the second displayed in the UI.
|
139
|
-
# If it's a proc,
|
141
|
+
# If it's a proc, that must return an Enumerable as above.
|
140
142
|
property :set
|
141
143
|
|
142
144
|
##
|
@@ -150,6 +152,7 @@ class Field
|
|
150
152
|
# Only for the distinct field type. The values will be sorted either with the
|
151
153
|
# most used values first (:frequency => true) or in
|
152
154
|
# alphabetical order (:description => true).
|
155
|
+
# FIXME re-implement this with Dataset
|
153
156
|
property :frequency, :description
|
154
157
|
|
155
158
|
##
|
@@ -160,7 +163,8 @@ class Field
|
|
160
163
|
|
161
164
|
##
|
162
165
|
# The property used for finding the field, ie by TableModel#field_column.
|
163
|
-
# Defaults to the attribute.
|
166
|
+
# Defaults to the attribute. If there are several display fields based on
|
167
|
+
# one db field, their attribute will be the same, but their id must be different.
|
164
168
|
property :id
|
165
169
|
|
166
170
|
##
|
@@ -168,11 +172,26 @@ class Field
|
|
168
172
|
# Either a proc( clevic_view, table_view, model_index ) or a symbol
|
169
173
|
# for a method( view, model_index ) on the Clevic::View object.
|
170
174
|
property :notify_data_changed
|
175
|
+
|
176
|
+
##
|
177
|
+
# This is the dataset of related objects.
|
178
|
+
# Called in configuration for a field that works with a relationship.
|
179
|
+
# dataset.filter( :blah => 'etc' ).order( :interesting_field )
|
180
|
+
def dataset
|
181
|
+
dataset_roller
|
182
|
+
end
|
183
|
+
|
184
|
+
# TODO Still getting the Builder/Built conflict
|
185
|
+
def dataset_roller
|
186
|
+
# related class if it's an association, entity_class otherwise
|
187
|
+
@dataset_roller ||= DatasetRoller.new( ( association? ? related_class : entity_class ).dataset )
|
188
|
+
end
|
171
189
|
|
172
190
|
# The list of properties for ActiveRecord options.
|
173
191
|
# There are actually from ActiveRecord::Base.VALID_FIND_OPTIONS, but it's protected.
|
174
192
|
# Each element becomes a property.
|
175
|
-
# TODO
|
193
|
+
# TODO deprecate these
|
194
|
+
# TODO warn or raise if these are used together with a dataset call
|
176
195
|
AR_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ]
|
177
196
|
AR_FIND_OPTIONS.each{|x| property x}
|
178
197
|
|
@@ -188,6 +207,10 @@ class Field
|
|
188
207
|
end
|
189
208
|
end
|
190
209
|
|
210
|
+
# The model object (eg TableModel) this field is part of.
|
211
|
+
# Set to TableModel by ModelBuilder#build
|
212
|
+
attr_accessor :model
|
213
|
+
|
191
214
|
# The UI delegate class for the field. The delegate class knows how to create a UI
|
192
215
|
# for this field using whatever GUI toolkit is selected
|
193
216
|
attr_accessor :delegate
|
@@ -243,36 +266,6 @@ EOF
|
|
243
266
|
default_display! if association?
|
244
267
|
end
|
245
268
|
|
246
|
-
# x_to_many fields are by definition collections of other entities
|
247
|
-
def many( &block )
|
248
|
-
if block
|
249
|
-
many_view( &block )
|
250
|
-
else
|
251
|
-
many_view do |mb|
|
252
|
-
# TODO should fetch this from one of the field definitions
|
253
|
-
mb.plain related_attribute
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
def many_builder
|
259
|
-
@many_view.builder
|
260
|
-
end
|
261
|
-
|
262
|
-
def many_fields
|
263
|
-
many_builder.fields
|
264
|
-
end
|
265
|
-
|
266
|
-
# return an instance of Clevic::View that represents the many items
|
267
|
-
# for this field
|
268
|
-
def many_view( &block )
|
269
|
-
@many_view ||= View.new( :entity_class => related_class, &block )
|
270
|
-
end
|
271
|
-
|
272
|
-
# The model object (eg TableModel) this field is part of.
|
273
|
-
# Set to TableModel by ModelBuilder#build
|
274
|
-
attr_accessor :model
|
275
|
-
|
276
269
|
# Return the attribute value for the given Object Relational Model instance, or nil
|
277
270
|
# if entity is nil. Will call transform_attribute.
|
278
271
|
def value_for( entity )
|
@@ -315,13 +308,6 @@ EOF
|
|
315
308
|
entity_class.meta[attribute]
|
316
309
|
end
|
317
310
|
|
318
|
-
# return the type of this attribute. Usually one of :string, :integer, :float
|
319
|
-
# or some entity class
|
320
|
-
# TODO remove
|
321
|
-
def attribute_type
|
322
|
-
meta.type
|
323
|
-
end
|
324
|
-
|
325
311
|
# return true if this field can be used in a filter
|
326
312
|
# virtual fields (ie those that don't exist in this field's
|
327
313
|
# table) can't be used to filter on.
|
@@ -337,7 +323,7 @@ EOF
|
|
337
323
|
# return the class object of a related class if this is a relational
|
338
324
|
# field, otherwise nil
|
339
325
|
def related_class
|
340
|
-
return nil unless entity_class.meta.has_key?( attribute )
|
326
|
+
return nil unless association? && entity_class.meta.has_key?( attribute )
|
341
327
|
@related_class ||= eval( entity_class.meta[attribute].class_name || attribute.to_s.classify )
|
342
328
|
end
|
343
329
|
|
@@ -363,26 +349,24 @@ EOF
|
|
363
349
|
do_generic_format( edit_format, value )
|
364
350
|
end
|
365
351
|
|
366
|
-
#
|
352
|
+
# Set or return a sample for the field which can be used to size the UI field widget.
|
367
353
|
def sample( *args )
|
368
354
|
if !args.empty?
|
369
355
|
@sample = args.first
|
370
356
|
self
|
371
357
|
else
|
372
358
|
if @sample.nil?
|
373
|
-
|
374
|
-
@sample
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
@sample ||= self.label
|
385
|
-
end
|
359
|
+
begin
|
360
|
+
@sample ||= Sampler.new( self ) do |value|
|
361
|
+
do_format( value )
|
362
|
+
end.compute
|
363
|
+
rescue
|
364
|
+
puts "for #{entity_class.name}"
|
365
|
+
puts $!.message
|
366
|
+
puts $!.backtrace
|
367
|
+
ensure
|
368
|
+
# if we don't know how to figure it out from the data, just return the label size
|
369
|
+
@sample ||= self.label
|
386
370
|
end
|
387
371
|
end
|
388
372
|
@sample
|
data/lib/clevic/field_valuer.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Clevic
|
2
|
-
#
|
3
|
-
#
|
2
|
+
# To be included in something that responds to entity and field methods.
|
3
|
+
# Used for getting values from the entity based on the definitions
|
4
4
|
# in the field.
|
5
5
|
module FieldValuer
|
6
6
|
|
@@ -26,13 +26,19 @@ module Clevic
|
|
26
26
|
self.attribute_value =
|
27
27
|
case
|
28
28
|
# allow flexibility in entering dates. For example
|
29
|
-
# 16jun, 16-jun, 16
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# year
|
29
|
+
# 16jun, 16-jun, 16/jun would be accepted here.
|
30
|
+
# Previous year is used if data capture is in January
|
31
|
+
# or February, and date.month is aug-dec.
|
33
32
|
when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{1,2})[ /-]?(\w{3})$}
|
34
|
-
|
35
|
-
|
33
|
+
today = Date.today
|
34
|
+
date = Date.parse( "#$1 #$2 #{Time.now.year.to_s}" )
|
35
|
+
|
36
|
+
# change year to last year
|
37
|
+
if (1..2).include?( today.month ) and (8..12).include?( date.month )
|
38
|
+
date = Date.civil( Date.today.year - 1, date.month, date.day )
|
39
|
+
end
|
40
|
+
date
|
41
|
+
|
36
42
|
# if a digit only is entered, fetch month and year from
|
37
43
|
# previous row
|
38
44
|
when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{1,2})$}
|
@@ -44,8 +50,10 @@ module Clevic
|
|
44
50
|
value
|
45
51
|
end
|
46
52
|
|
47
|
-
#
|
53
|
+
# Mostly to fix date strings that have come
|
48
54
|
# out of the db and been formatted
|
55
|
+
# Accepts dd mmm yy.
|
56
|
+
# Assumes 20 as the century.
|
49
57
|
when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{2})[ /-](\w{3})[ /-](\d{2})$}
|
50
58
|
Date.parse( "#$1 #$2 20#$3" )
|
51
59
|
|
@@ -60,6 +68,7 @@ module Clevic
|
|
60
68
|
# do various transforms
|
61
69
|
case
|
62
70
|
# accept a space or a comma instead of a . for floats
|
71
|
+
# as long as there are only 2 decimal places
|
63
72
|
when value =~ /(.*?)(\d)[ ,](\d{2})$/
|
64
73
|
"#$1#$2.#$3"
|
65
74
|
else
|
@@ -72,7 +81,7 @@ module Clevic
|
|
72
81
|
end
|
73
82
|
|
74
83
|
def find_related( attribute, value )
|
75
|
-
candidates = field.related_class.
|
84
|
+
candidates = field.related_class.filter( attribute.to_sym => value ).all
|
76
85
|
if candidates.size != 1
|
77
86
|
raise "#{candidates.size} != 1 candidates for #{value}: #{candidates.inspect}"
|
78
87
|
end
|
@@ -152,6 +161,10 @@ module Clevic
|
|
152
161
|
end
|
153
162
|
end
|
154
163
|
|
164
|
+
def valuer( entity )
|
165
|
+
Valuer.new(field,entity)
|
166
|
+
end
|
167
|
+
|
155
168
|
def self.valuer( field, entity )
|
156
169
|
Valuer.new(field,entity)
|
157
170
|
end
|
@@ -1,41 +1,32 @@
|
|
1
1
|
module Clevic
|
2
2
|
class FilterCommand
|
3
|
-
#
|
4
|
-
#
|
5
|
-
def initialize( table_view,
|
3
|
+
# filter_block will be passed a Dataset to filter.
|
4
|
+
# filter_message will be displayed.
|
5
|
+
def initialize( table_view, message, &filter_block )
|
6
6
|
@table_view = table_view
|
7
|
-
@
|
8
|
-
@
|
9
|
-
|
10
|
-
# Better make the status message now, before the indexes become invalid
|
11
|
-
@status_message =
|
12
|
-
if filter_indexes.empty?
|
13
|
-
# no indexes, so use filter_conditions.
|
14
|
-
"Filtered on #{filter_conditions.inspect}"
|
15
|
-
else
|
16
|
-
"Filtered on #{filter_indexes.first.field.label} = #{filter_indexes.first.display_value}"
|
17
|
-
end
|
7
|
+
@message = message
|
8
|
+
@filter_block = filter_block
|
18
9
|
end
|
19
10
|
|
11
|
+
attr_reader :message
|
12
|
+
|
20
13
|
# Do the filtering. Return true if successful, false otherwise.
|
21
14
|
def doit
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@table_view.model.reload_data( @filter_conditions )
|
32
|
-
rescue Exception => e
|
33
|
-
puts
|
34
|
-
puts e.message
|
35
|
-
puts e.backtrace
|
36
|
-
false
|
37
|
-
end
|
15
|
+
# store current dataset
|
16
|
+
@previous_dataset = @table_view.model.cache_table.dataset
|
17
|
+
|
18
|
+
# store auto_new
|
19
|
+
@auto_new = @table_view.model.auto_new
|
20
|
+
|
21
|
+
# reload cache table with new conditions
|
22
|
+
@table_view.model.auto_new = false
|
23
|
+
@table_view.model.reload_data( &@filter_block )
|
38
24
|
true
|
25
|
+
rescue Exception => e
|
26
|
+
puts
|
27
|
+
puts e.message
|
28
|
+
puts e.backtrace
|
29
|
+
false
|
39
30
|
end
|
40
31
|
|
41
32
|
def undo
|
@@ -43,12 +34,7 @@ module Clevic
|
|
43
34
|
@table_view.model.auto_new = @auto_new
|
44
35
|
|
45
36
|
# reload cache table with stored AR conditions
|
46
|
-
@table_view.model.reload_data( @
|
47
|
-
end
|
48
|
-
|
49
|
-
# return a message based on the conditions
|
50
|
-
def status_message
|
51
|
-
@status_message
|
37
|
+
@table_view.model.reload_data( @previous_dataset )
|
52
38
|
end
|
53
39
|
end
|
54
40
|
end
|