clevic 0.13.0.b3 → 0.13.0.b5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,37 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
To handle multiple GUI frameworks, Clevic makes use of
|
3
|
+
Ruby's open classes. Whenever there is a class that
|
4
|
+
interacts with a GUI framework (say Qt, or Java Swing)
|
5
|
+
the framework-specific part of the class is loaded
|
6
|
+
first to get access to the framework's inheritance
|
7
|
+
hierarchy, then the file with the framework-neutral
|
8
|
+
code is loaded. The code below helps to check
|
9
|
+
that the relevant methods from framework-neutral code
|
10
|
+
are already defined by the time the framework-neutral
|
11
|
+
class definition is executed.
|
12
|
+
=end
|
13
|
+
class Class
|
14
|
+
|
15
|
+
# method_name can be a symbol or a string.
|
16
|
+
#
|
17
|
+
# If a method of this name doesn't already exist
|
18
|
+
# add it, so that if when it's called later it raises
|
19
|
+
# and exception. Otherwise if the named method already
|
20
|
+
# exists, just leave it alone.
|
21
|
+
def framework_responsibility( method_name )
|
22
|
+
unless instance_methods.include?( method_name.to_s )
|
23
|
+
define_method method_name do
|
24
|
+
raise "Framework-specific code has not defined for for #{self.class}##{method_name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def subclass_responsibility( method_name )
|
30
|
+
unless instance_methods.include?( method_name.to_s )
|
31
|
+
define_method( method_name ) do
|
32
|
+
raise "#{method_name} is subclass responsibility for #{self.class}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Clevic
|
2
2
|
|
3
|
-
#
|
3
|
+
# Format values for display / edit. Essentially a common interface
|
4
|
+
# for % for Strings and strftime for Dates and Times.
|
5
|
+
#
|
6
|
+
# includers must provide meta and display
|
4
7
|
module GenericFormat
|
5
8
|
# Return true if the field is a date, datetime, time or timestamp.
|
6
9
|
# If display is nil, the value is calculated, so we need
|
@@ -38,6 +41,7 @@ module GenericFormat
|
|
38
41
|
value
|
39
42
|
end
|
40
43
|
rescue Exception => e
|
44
|
+
puts "self: #{self.inspect}"
|
41
45
|
puts "format: #{format.inspect}"
|
42
46
|
puts "value.class: #{value.class.inspect}"
|
43
47
|
puts "value: #{value.inspect}"
|
data/lib/clevic/many_field.rb
CHANGED
@@ -1,7 +1,32 @@
|
|
1
1
|
module Clevic
|
2
2
|
|
3
|
-
#
|
4
|
-
|
5
|
-
|
3
|
+
# Preliminary code for multi-valued fields. Not working yet.
|
4
|
+
module ManyField
|
5
|
+
# x_to_many fields are by definition collections of other entities
|
6
|
+
def many( &block )
|
7
|
+
if block
|
8
|
+
many_view( &block )
|
9
|
+
else
|
10
|
+
many_view do |mb|
|
11
|
+
# TODO should fetch this from one of the field definitions
|
12
|
+
mb.plain related_attribute
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def many_builder
|
18
|
+
@many_view.builder
|
19
|
+
end
|
20
|
+
|
21
|
+
def many_fields
|
22
|
+
many_builder.fields
|
23
|
+
end
|
24
|
+
|
25
|
+
# return an instance of Clevic::View that represents the many items
|
26
|
+
# for this field
|
27
|
+
def many_view( &block )
|
28
|
+
@many_view ||= View.new( :entity_class => related_class, &block )
|
29
|
+
end
|
30
|
+
end
|
6
31
|
|
7
32
|
end
|
data/lib/clevic/model_builder.rb
CHANGED
@@ -513,15 +513,17 @@ class ModelBuilder
|
|
513
513
|
field.delegate = BooleanDelegate.new( field )
|
514
514
|
end
|
515
515
|
|
516
|
-
#
|
517
|
-
#
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
516
|
+
# specify the dataset but just calling and chaining, thusly
|
517
|
+
# dataset.order( :some_field ).filter( :active => true )
|
518
|
+
def dataset
|
519
|
+
@dataset_roller = DatasetRoller.new( entity_class.dataset )
|
520
|
+
end
|
521
|
+
|
522
|
+
def records( *args )
|
523
|
+
puts "ModelBuilder#records is deprecated. Use ModelBuilder#dataset instead"
|
524
|
+
require 'clevic/sequel_ar_adapter.rb'
|
525
|
+
entity_class.plugin :ar_methods
|
526
|
+
@cache_table = CacheTable.new( entity_class, entity_class.translate( args.first ) )
|
525
527
|
end
|
526
528
|
|
527
529
|
# Tell this field not to show up in the UI.
|
@@ -560,7 +562,15 @@ class ModelBuilder
|
|
560
562
|
entity_class.attributes.each do |column,model_column|
|
561
563
|
begin
|
562
564
|
if model_column.association?
|
563
|
-
relational column
|
565
|
+
relational column do |f|
|
566
|
+
# TODO this should be tableize or equivalent
|
567
|
+
%W{#{model_column.related_class.name.downcase} name title username}.each do |name|
|
568
|
+
if model_column.related_class.instance_methods.include?( name )
|
569
|
+
f.display = name.to_sym
|
570
|
+
break
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
564
574
|
else
|
565
575
|
plain column
|
566
576
|
end
|
@@ -572,7 +582,6 @@ class ModelBuilder
|
|
572
582
|
plain column
|
573
583
|
end
|
574
584
|
end
|
575
|
-
records :order => entity_class.primary_key
|
576
585
|
end
|
577
586
|
|
578
587
|
# return the named Clevic::Field object
|
@@ -605,7 +614,7 @@ class ModelBuilder
|
|
605
614
|
end
|
606
615
|
|
607
616
|
# the data
|
608
|
-
@model.collection =
|
617
|
+
@model.collection = create_cache_table
|
609
618
|
|
610
619
|
@model
|
611
620
|
end
|
@@ -632,34 +641,20 @@ protected
|
|
632
641
|
when entity_class.instance_methods.include?( attribute.to_s )
|
633
642
|
# read-only if there's no setter for the attribute
|
634
643
|
!entity_class.instance_methods.include?( "#{attribute.to_s}=" )
|
644
|
+
|
635
645
|
else
|
636
646
|
# default to not read-only
|
637
647
|
false
|
638
648
|
end
|
639
649
|
end
|
640
650
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
# Called by records( *args )
|
645
|
-
def set_records( arg )
|
646
|
-
if arg.class == Hash
|
647
|
-
# need to defer this until all fields are collected
|
648
|
-
@find_options = arg
|
649
|
-
else
|
650
|
-
@records = arg
|
651
|
+
def create_cache_table
|
652
|
+
if @dataset_roller
|
653
|
+
@cache_table = CacheTable.new( entity_class, @dataset_roller.dataset )
|
651
654
|
end
|
655
|
+
# otherwise just default it
|
656
|
+
@cache_table ||= CacheTable.new( entity_class )
|
652
657
|
end
|
653
|
-
|
654
|
-
# Return a collection of records. Usually this will be a CacheTable.
|
655
|
-
# Called by records( *args )
|
656
|
-
def get_records
|
657
|
-
if @records.nil?
|
658
|
-
@records = CacheTable.new( entity_class, @find_options )
|
659
|
-
end
|
660
|
-
@records
|
661
|
-
end
|
662
|
-
|
663
658
|
end
|
664
659
|
|
665
660
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Clevic
|
2
|
+
|
3
|
+
# Provides a nice way of getting to Sequel::Dataset's
|
4
|
+
# opts[:order] information
|
5
|
+
#
|
6
|
+
# Including class must call dataset= before calling order_attributes
|
7
|
+
module OrderedDataset
|
8
|
+
|
9
|
+
# returns a collection of [ attribute, (1|-1) ]
|
10
|
+
# where 1 is forward/asc (>) and -1 is backward/desc (<)
|
11
|
+
def order_attributes
|
12
|
+
if @order_attributes.nil?
|
13
|
+
@order_attributes =
|
14
|
+
dataset.opts[:order].map do |order_expr|
|
15
|
+
case order_expr
|
16
|
+
when Symbol
|
17
|
+
[ order_expr, 1 ]
|
18
|
+
|
19
|
+
when Sequel::SQL::OrderedExpression
|
20
|
+
[ order_expr.expression, order_expr.descending ? -1 : 1 ]
|
21
|
+
|
22
|
+
else
|
23
|
+
raise "unknown order_expr: #{order_expr.inspect}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
@order_attributes
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :dataset
|
31
|
+
|
32
|
+
# Set default dataset ordering to primary key if it doesn't
|
33
|
+
# already have an order.
|
34
|
+
def dataset=( other )
|
35
|
+
@dataset =
|
36
|
+
if other.opts[:order].nil?
|
37
|
+
other.order( other.model.primary_key )
|
38
|
+
else
|
39
|
+
other
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/clevic/qt.rb
CHANGED
@@ -2,13 +2,16 @@
|
|
2
2
|
Require this file to do Clevic in Qt
|
3
3
|
=end
|
4
4
|
|
5
|
-
require '
|
5
|
+
require 'clevic/framework'
|
6
|
+
|
7
|
+
require 'Qt4'
|
6
8
|
|
7
9
|
# require these first, so TableModel and TableView get the correct ancestors
|
8
10
|
require 'clevic/qt/table_model.rb'
|
9
11
|
require 'clevic/qt/table_view.rb'
|
10
12
|
|
11
13
|
# all other files in the qt subdirectory
|
14
|
+
require 'pathname'
|
12
15
|
( Pathname.new( __FILE__ ).parent + 'qt' ).children.grep( /.rb$/ ).each do |child|
|
13
16
|
require child.to_s
|
14
17
|
end
|
@@ -35,7 +35,15 @@ module ActionBuilder
|
|
35
35
|
unless block.nil?
|
36
36
|
action_triggered do
|
37
37
|
qt_action.connect SIGNAL( signal_name ) do |active|
|
38
|
-
|
38
|
+
# need this rescue here because otherwise Qt
|
39
|
+
# doesn't catch it and the whole app comes down
|
40
|
+
begin
|
41
|
+
yield( active )
|
42
|
+
rescue
|
43
|
+
# TODO how to display this in the UI?
|
44
|
+
puts $!.message
|
45
|
+
puts $!.backtrace
|
46
|
+
end
|
39
47
|
end
|
40
48
|
end
|
41
49
|
end
|
data/lib/clevic/qt/browser.rb
CHANGED
@@ -154,7 +154,7 @@ class Browser < Qt::Widget
|
|
154
154
|
# handle filter status changed, so we can provide a visual indication
|
155
155
|
tab.connect SIGNAL( 'filter_status_signal(bool)' ) do |status|
|
156
156
|
# update the tab, so there's a visual indication of filtering
|
157
|
-
filter_title = ( tab.filtered ? '| ' : '' ) + translate( tab.title )
|
157
|
+
filter_title = ( tab.filtered? ? '| ' : '' ) + translate( tab.title )
|
158
158
|
tables_tab.set_tab_text( tables_tab.current_index, filter_title )
|
159
159
|
end
|
160
160
|
rescue Exception => e
|
data/lib/clevic/qt/clipboard.rb
CHANGED
@@ -24,9 +24,9 @@ module Clevic
|
|
24
24
|
system.mime_data.has_html
|
25
25
|
end
|
26
26
|
|
27
|
-
# TODO figure out why Qt never has anything other than text
|
28
|
-
#
|
29
|
-
# from irb
|
27
|
+
# TODO figure out why Qt never has anything other than text.
|
28
|
+
# Could be because the event loop isn't running when testing
|
29
|
+
# from irb.
|
30
30
|
def html
|
31
31
|
system.mime_data.html
|
32
32
|
end
|
@@ -1,4 +1,5 @@
|
|
1
|
-
require 'clevic/qt/
|
1
|
+
require 'clevic/qt/delegate.rb'
|
2
|
+
require 'clevic/qt/qt_combo_box.rb'
|
2
3
|
|
3
4
|
module Clevic
|
4
5
|
|
@@ -8,12 +9,9 @@ because ComboBox stupidly doesn't.
|
|
8
9
|
|
9
10
|
Generally these will be created using a Clevic::ModelBuilder.
|
10
11
|
=end
|
11
|
-
class ComboDelegate < Clevic::
|
12
|
-
def initialize( field )
|
13
|
-
super
|
14
|
-
end
|
15
|
-
|
12
|
+
class ComboDelegate < Clevic::Delegate
|
16
13
|
# Convert Qt:: constants from the integer value to a string value.
|
14
|
+
# TODO this really shouldn't be here. qtext, or extensions.rb
|
17
15
|
def hint_string( hint )
|
18
16
|
hs = String.new
|
19
17
|
Qt::AbstractItemDelegate.constants.each do |x|
|
@@ -38,102 +36,38 @@ class ComboDelegate < Clevic::ItemDelegate
|
|
38
36
|
|
39
37
|
# open the combo box, just like if f4 was pressed
|
40
38
|
def full_edit
|
41
|
-
if is_combo?(
|
42
|
-
@editor.show_popup
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# returns true if the editor allows values outside of a predefined
|
47
|
-
# range, false otherwise.
|
48
|
-
def restricted?
|
49
|
-
false
|
50
|
-
end
|
51
|
-
|
52
|
-
# TODO fetch this from the model definition
|
53
|
-
def allow_null?
|
54
|
-
true
|
55
|
-
end
|
56
|
-
|
57
|
-
# Subclasses should override this to fill the combo box
|
58
|
-
# list with values.
|
59
|
-
def populate( editor, model_index )
|
60
|
-
raise "subclass responsibility"
|
39
|
+
editor.show_popup if is_combo?( editor )
|
61
40
|
end
|
62
41
|
|
63
|
-
# return true if this delegate needs a combo, false otherwise
|
64
|
-
def needs_combo?
|
65
|
-
raise "subclass responsibility"
|
66
|
-
end
|
67
|
-
|
68
42
|
def is_combo?( editor )
|
69
|
-
editor.
|
70
|
-
end
|
71
|
-
|
72
|
-
# return true if this field has no data (needs_combo? is false)
|
73
|
-
# and is at the same time restricted (ie needs data from somewhere else)
|
74
|
-
def empty_set?
|
75
|
-
!needs_combo? && restricted?
|
43
|
+
editor.is_a?( Qt::ComboBox )
|
76
44
|
end
|
77
45
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
83
|
-
|
84
|
-
# if this delegate has an empty set, return the message, otherwise
|
85
|
-
# return nil.
|
86
|
-
def if_empty_message
|
87
|
-
if empty_set?
|
88
|
-
empty_set_message
|
46
|
+
def create_combo_box( *args )
|
47
|
+
Qt::ComboBox.new( parent ).tap do |combo|
|
48
|
+
# all combos are editable so that prefix matching will work
|
49
|
+
combo.editable = true
|
89
50
|
end
|
90
51
|
end
|
91
52
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
53
|
+
# Override the Qt method. Create a ComboBox widget and fill it with the possible values.
|
54
|
+
def createEditor( parent_widget, style_option_view_item, model_index )
|
55
|
+
self.parent = parent_widget
|
56
|
+
self.entity = model_index.entity
|
57
|
+
init_component( parent_widget, style_option_view_item, model_index )
|
58
|
+
editor.delegate = self
|
59
|
+
editor
|
98
60
|
end
|
99
61
|
|
100
|
-
def
|
101
|
-
|
102
|
-
editor.add_item( '', nil.to_variant )
|
103
|
-
end
|
62
|
+
def line_editor( edit_value )
|
63
|
+
@line_editor ||= Qt::LineEdit.new( edit_value, parent )
|
104
64
|
end
|
105
65
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
@editor = Qt::ComboBox.new( parent_widget )
|
110
|
-
|
111
|
-
# subclasses fill in the rest of the entries
|
112
|
-
populate( @editor, model_index )
|
113
|
-
|
114
|
-
# add the current item, if it isn't there already
|
115
|
-
populate_current( @editor, model_index )
|
116
|
-
|
117
|
-
# create a nil entry
|
118
|
-
add_nil_item( @editor ) if allow_null?
|
119
|
-
|
120
|
-
# allow prefix matching from the keyboard
|
121
|
-
@editor.editable = true
|
122
|
-
|
123
|
-
# don't insert if restricted
|
124
|
-
@editor.insert_policy = Qt::ComboBox::NoInsert if restricted?
|
125
|
-
else
|
126
|
-
@editor =
|
127
|
-
if restricted?
|
128
|
-
emit parent.status_text( empty_set_message )
|
129
|
-
nil
|
130
|
-
else
|
131
|
-
Qt::LineEdit.new( model_index.edit_value, parent_widget )
|
132
|
-
end
|
133
|
-
end
|
134
|
-
@editor
|
66
|
+
def framework_setup( *args )
|
67
|
+
# don't need to do anything here
|
68
|
+
# might need to once prefix-matching is implemented
|
135
69
|
end
|
136
|
-
|
70
|
+
|
137
71
|
# Override the Qt::ItemDelegate method.
|
138
72
|
def updateEditorGeometry( editor, style_option_view_item, model_index )
|
139
73
|
rect = style_option_view_item.rect
|
@@ -196,3 +130,5 @@ class ComboDelegate < Clevic::ItemDelegate
|
|
196
130
|
end
|
197
131
|
|
198
132
|
end
|
133
|
+
|
134
|
+
require 'clevic/delegates/combo_delegate'
|