clevic 0.13.0.b9 → 0.13.0.b10

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.
Files changed (85) hide show
  1. data/History.txt +3 -0
  2. data/lib/clevic/action_builder.rb +16 -16
  3. data/lib/clevic/ar_methods.rb +22 -22
  4. data/lib/clevic/attribute_list.rb +5 -5
  5. data/lib/clevic/cache_table.rb +18 -18
  6. data/lib/clevic/dataset_roller.rb +3 -3
  7. data/lib/clevic/default_view.rb +4 -4
  8. data/lib/clevic/delegate.rb +8 -8
  9. data/lib/clevic/delegates/combo_delegate.rb +22 -22
  10. data/lib/clevic/delegates/distinct_delegate.rb +5 -5
  11. data/lib/clevic/delegates/relational_delegate.rb +6 -6
  12. data/lib/clevic/delegates/set_delegate.rb +3 -3
  13. data/lib/clevic/dirty.rb +1 -1
  14. data/lib/clevic/emitter.rb +3 -3
  15. data/lib/clevic/extensions.rb +4 -4
  16. data/lib/clevic/field.rb +68 -68
  17. data/lib/clevic/field_valuer.rb +26 -26
  18. data/lib/clevic/filter_command.rb +6 -6
  19. data/lib/clevic/framework.rb +3 -3
  20. data/lib/clevic/generic_format.rb +2 -2
  21. data/lib/clevic/many_field.rb +3 -3
  22. data/lib/clevic/model_builder.rb +88 -84
  23. data/lib/clevic/model_column.rb +13 -13
  24. data/lib/clevic/ordered_dataset.rb +7 -7
  25. data/lib/clevic/qt/action_builder.rb +3 -3
  26. data/lib/clevic/qt/browser.rb +28 -28
  27. data/lib/clevic/qt/clipboard.rb +5 -5
  28. data/lib/clevic/qt/combo_delegate.rb +12 -12
  29. data/lib/clevic/qt/distinct_delegate.rb +1 -1
  30. data/lib/clevic/qt/extensions.rb +4 -4
  31. data/lib/clevic/qt/qt_combo_box.rb +7 -7
  32. data/lib/clevic/qt/relational_delegate.rb +5 -5
  33. data/lib/clevic/qt/search_dialog.rb +15 -15
  34. data/lib/clevic/qt/simplest_delegate.rb +2 -2
  35. data/lib/clevic/qt/table_model.rb +46 -46
  36. data/lib/clevic/qt/table_view.rb +48 -48
  37. data/lib/clevic/qt/text_delegate.rb +9 -9
  38. data/lib/clevic/rails_models_loaders.rb +3 -3
  39. data/lib/clevic/record.rb +8 -8
  40. data/lib/clevic/sampler.rb +6 -6
  41. data/lib/clevic/sequel_ar_adapter.rb +22 -22
  42. data/lib/clevic/sequel_clevic.rb +10 -10
  43. data/lib/clevic/sequel_meta.rb +5 -5
  44. data/lib/clevic/sequel_naked.rb +4 -4
  45. data/lib/clevic/swing/action.rb +20 -20
  46. data/lib/clevic/swing/action_builder.rb +2 -2
  47. data/lib/clevic/swing/boolean_delegate.rb +3 -3
  48. data/lib/clevic/swing/browser.rb +37 -37
  49. data/lib/clevic/swing/cell_editor.rb +13 -13
  50. data/lib/clevic/swing/cell_renderer.rb +7 -7
  51. data/lib/clevic/swing/clipboard.rb +19 -19
  52. data/lib/clevic/swing/combo_delegate.rb +26 -26
  53. data/lib/clevic/swing/confirm_dialog.rb +7 -7
  54. data/lib/clevic/swing/delegate.rb +4 -4
  55. data/lib/clevic/swing/extensions.rb +24 -24
  56. data/lib/clevic/swing/field.rb +1 -1
  57. data/lib/clevic/swing/relational_delegate.rb +2 -2
  58. data/lib/clevic/swing/row_header.rb +32 -32
  59. data/lib/clevic/swing/search_dialog.rb +31 -31
  60. data/lib/clevic/swing/selection_model.rb +12 -12
  61. data/lib/clevic/swing/swing_table_index.rb +6 -6
  62. data/lib/clevic/swing/table_model.rb +30 -30
  63. data/lib/clevic/swing/table_view.rb +54 -54
  64. data/lib/clevic/swing/table_view_focus.rb +4 -4
  65. data/lib/clevic/swing/tag_delegate.rb +14 -14
  66. data/lib/clevic/swing/tag_editor.rb +4 -4
  67. data/lib/clevic/swing/text_area_delegate.rb +6 -6
  68. data/lib/clevic/swing/text_delegate.rb +4 -4
  69. data/lib/clevic/table_index.rb +14 -14
  70. data/lib/clevic/table_model.rb +30 -30
  71. data/lib/clevic/table_searcher.rb +19 -19
  72. data/lib/clevic/table_view.rb +92 -92
  73. data/lib/clevic/table_view_paste.rb +19 -19
  74. data/lib/clevic/version.rb +1 -1
  75. data/lib/clevic/view.rb +22 -22
  76. data/models/accounts_models.rb +10 -10
  77. data/models/examples.rb +2 -2
  78. data/models/times_models.rb +32 -32
  79. data/models/values_models.rb +2 -2
  80. data/test/test_cache_table.rb +15 -15
  81. data/test/test_helper.rb +7 -7
  82. data/test/test_model_index_extensions.rb +6 -6
  83. data/test/test_table_model.rb +6 -6
  84. data/test/test_table_searcher.rb +25 -25
  85. metadata +33 -35
@@ -8,14 +8,14 @@ class TagEditor < javax.swing.JComponent
8
8
  def initialize( field )
9
9
  super()
10
10
  @field = field
11
-
11
+
12
12
  create_fields
13
13
  init_layout
14
14
  end
15
-
15
+
16
16
  attr_reader :field
17
17
  attr_reader :entity
18
-
18
+
19
19
  # Hopefully called by the editor framework
20
20
  # might be init_component
21
21
  def configureEditor( editor, entity )
@@ -34,7 +34,7 @@ class TagEditor < javax.swing.JComponent
34
34
  @add_button = javax.swing.JButton.new
35
35
  @remove_button = javax.swing.JButton.new
36
36
  end
37
-
37
+
38
38
  def init_layout
39
39
  # This is mostly cut-n-pasted from the Netbeans Java sources. So don't tweak it.
40
40
  text_field.setName("text_field");
@@ -12,7 +12,7 @@ module Clevic
12
12
  super( newrect )
13
13
  end
14
14
  end
15
-
15
+
16
16
  class TextAreaDelegate < Delegate
17
17
  # TODO check that Ctrl-VK_ENTER stops editing
18
18
  def init_component( cell_editor )
@@ -21,23 +21,23 @@ module Clevic
21
21
  text_component.rows = ( edit_value.andand.count( "\n" ) || 0 ) + 2
22
22
  text_component.select_all
23
23
  end
24
-
24
+
25
25
  def editor
26
26
  @editor ||= EditorScrollPane.new( text_component, javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS )
27
27
  end
28
-
28
+
29
29
  def text_component
30
30
  @text_component ||= javax.swing.JTextArea.new
31
31
  end
32
-
32
+
33
33
  def value
34
34
  text_component.text
35
35
  end
36
-
36
+
37
37
  def minimal_edit
38
38
  text_component.select_all
39
39
  end
40
-
40
+
41
41
  def needs_pre_selection?
42
42
  true
43
43
  end
@@ -8,21 +8,21 @@ module Clevic
8
8
  editor.text = edit_value
9
9
  editor.select_all
10
10
  end
11
-
11
+
12
12
  def editor
13
13
  @editor ||= javax.swing.JTextField.new.tap do |e|
14
14
  e.horizontal_alignment = field.swing_alignment
15
15
  end
16
16
  end
17
-
17
+
18
18
  def value
19
19
  editor.text
20
20
  end
21
-
21
+
22
22
  def minimal_edit
23
23
  editor.select_all
24
24
  end
25
-
25
+
26
26
  def needs_pre_selection?
27
27
  true
28
28
  end
@@ -4,12 +4,12 @@ module Clevic
4
4
  # to be included in something that responds to model, row, column
5
5
  module TableIndex
6
6
  include FieldValuer
7
-
7
+
8
8
  # return the Clevic::Field for this index
9
9
  def field
10
10
  @field ||= model.field_for_index( self )
11
11
  end
12
-
12
+
13
13
  def dump
14
14
  if valid?
15
15
  <<-EOF
@@ -21,35 +21,35 @@ module Clevic
21
21
  'invalid'
22
22
  end
23
23
  end
24
-
24
+
25
25
  def prev
26
26
  choppy( :row => row - 1 )
27
27
  end
28
-
28
+
29
29
  # return the attribute of the underlying entity corresponding
30
30
  # to the column of this index
31
31
  def attribute
32
32
  model.attributes[column]
33
33
  end
34
-
34
+
35
35
  # returns the list of ModelColumn metadata
36
36
  def meta
37
37
  # use the optimised version
38
38
  # TODO just use the model version instead?
39
39
  field.meta
40
40
  end
41
-
41
+
42
42
  # return the table's field name. For associations, this would
43
43
  # be suffixed with _id
44
44
  def field_name
45
45
  meta.name
46
46
  end
47
-
47
+
48
48
  # return the value of the field, it may be the _id value
49
49
  def field_value
50
50
  entity.send( field_name )
51
51
  end
52
-
52
+
53
53
  # the underlying entity
54
54
  # TODO caching doesn't help with Qt because ModelIndex objects are
55
55
  # extremely short-lived. Not sure about swing.
@@ -57,9 +57,9 @@ module Clevic
57
57
  return nil if model.nil?
58
58
  model.collection[row]
59
59
  end
60
-
60
+
61
61
  attr_writer :entity
62
-
62
+
63
63
  # return true if validation failed for this indexes field
64
64
  def has_errors?
65
65
  # virtual fields don't have metadata
@@ -69,14 +69,14 @@ module Clevic
69
69
  entity.errors.invalid?( field_name.to_sym )
70
70
  end
71
71
  end
72
-
72
+
73
73
  # return a collection of errors. Unlike AR, this
74
74
  # will always return an array that will have zero, one
75
75
  # or many elements.
76
76
  def errors
77
77
  [ entity.errors[field_name.to_sym] ].flatten
78
78
  end
79
-
79
+
80
80
  # sort by row, then column
81
81
  def <=>( other )
82
82
  row_comp = self.row <=> other.row
@@ -86,11 +86,11 @@ module Clevic
86
86
  row_comp
87
87
  end
88
88
  end
89
-
89
+
90
90
  def inspect
91
91
  "#<TableIndex (#{row},#{column}) '#{raw_value rescue "no raw value: #{$!.message}"}'>"
92
92
  end
93
-
93
+
94
94
  # return a string (row,column)
95
95
  # suitable for displaying to users, ie 1-based not 0-based
96
96
  def rc
@@ -15,10 +15,10 @@ class TableModel
15
15
  # the CacheTable of Clevic::Record or Sequel::Model objects
16
16
  attr_reader :collection
17
17
  alias_method :cache_table, :collection
18
-
18
+
19
19
  # the collection of Clevic::Field objects
20
20
  attr_reader :fields
21
-
21
+
22
22
  attr_accessor :read_only
23
23
  def read_only?; read_only; end
24
24
 
@@ -26,69 +26,69 @@ class TableModel
26
26
  attr_accessor :auto_new
27
27
  def auto_new?; auto_new; end
28
28
  def auto_new?; auto_new; end
29
-
29
+
30
30
  attr_accessor :entity_view
31
31
  attr_accessor :builder
32
-
32
+
33
33
  # If somre or all the entities in the collection is related to a single entity
34
34
  # somewhere else, this is it. Not sure if this is the right
35
35
  # way to do it, but try anyway.
36
36
  attr_accessor :one
37
-
37
+
38
38
  def entity_class
39
39
  entity_view.entity_class
40
40
  end
41
-
41
+
42
42
  def fields=( arr )
43
43
  @fields = arr
44
-
44
+
45
45
  # reset these
46
46
  @labels = nil
47
47
  @attributes = nil
48
48
  end
49
-
49
+
50
50
  # field is a symbol or string referring to a column.
51
51
  # returns the index of that field.
52
52
  def field_column( field )
53
53
  fields.each_with_index {|x,i| return i if x.id == field.to_sym }
54
54
  end
55
-
55
+
56
56
  def field_for_index( model_index )
57
57
  fields[model_index.column]
58
58
  end
59
-
59
+
60
60
  def labels
61
61
  @labels ||= fields.map {|x| x.label }
62
62
  end
63
-
63
+
64
64
  def attributes
65
65
  @attributes ||= fields.map {|x| x.attribute }
66
66
  end
67
-
67
+
68
68
  def collection=( arr )
69
69
  @collection = arr
70
70
  # fill in an empty record for data entry
71
71
  add_new_item if collection.empty? && auto_new?
72
72
  end
73
-
73
+
74
74
  # add a new item, and set defaults from the Clevic::View
75
75
  # add_new_item_start and add_new_item_end are provided by
76
76
  # the GUI framework-specific class
77
77
  def add_new_item
78
78
  add_new_item_start
79
-
79
+
80
80
  entity = entity_class.new
81
-
81
+
82
82
  # set default values without triggering changed
83
83
  fields.each do |f|
84
84
  f.set_default_for( entity ) unless f.default.nil?
85
85
  end
86
-
86
+
87
87
  collection << entity
88
-
88
+
89
89
  add_new_item_end
90
90
  end
91
-
91
+
92
92
  # rows is a collection of integers specifying row indices to remove
93
93
  def remove_rows( rows )
94
94
  # don't delete rows twice
@@ -99,25 +99,25 @@ class TableModel
99
99
  removals = rows_in_order.map do |index|
100
100
  collection[index]
101
101
  end
102
-
102
+
103
103
  remove_notify( rows_in_order ) do
104
104
  removals.each do |to_remove, index|
105
105
  # destroy the db object, and its associated table row
106
106
  to_remove.destroy unless to_remove.nil? || to_remove.new?
107
107
  end
108
108
  end
109
-
109
+
110
110
  # because it's too bloody complicated to figure out which items
111
111
  # were deleted and then remove them from the collection. And there
112
112
  # most likely isn't a big hit for doing this, unless there's a lot
113
113
  # of cached calcuation in the entities.
114
114
  self.collection = collection.renew
115
-
115
+
116
116
  # create a new row if auto_new is on
117
117
  # should really be in a signal handler
118
118
  add_new_item if collection.empty? && auto_new?
119
119
  end
120
-
120
+
121
121
  # save the model at the given index, if it's dirty
122
122
  # TODO Sequel uses modified?
123
123
  def save( index )
@@ -141,7 +141,7 @@ class TableModel
141
141
  puts $!.backtrace
142
142
  emit_data_error( index, nil, $!.message )
143
143
  end
144
-
144
+
145
145
  def reload_data( dataset = nil, &dataset_block )
146
146
  # renew cache. All records will be dropped and reloaded.
147
147
  self.collection = self.cache_table.renew( dataset, &dataset_block )
@@ -159,7 +159,7 @@ class TableModel
159
159
  start_index.field
160
160
  )
161
161
  entity = searcher.search( start_index.entity )
162
-
162
+
163
163
  # return matched indexes
164
164
  unless entity.nil?
165
165
  found_row = collection.index_for_entity( entity )
@@ -169,12 +169,12 @@ class TableModel
169
169
  []
170
170
  end
171
171
  end
172
-
172
+
173
173
  class DataChange
174
174
  class ModelIndexProxy
175
175
  attr_accessor :row
176
176
  attr_accessor :column
177
-
177
+
178
178
  def initialize( other = nil )
179
179
  unless other.nil?
180
180
  @row = other.row
@@ -182,18 +182,18 @@ class TableModel
182
182
  end
183
183
  end
184
184
  end
185
-
185
+
186
186
  def top_left
187
187
  @top_left ||= ModelIndexProxy.new
188
188
  end
189
-
189
+
190
190
  def bottom_right
191
191
  @bottom_right ||= ModelIndexProxy.new
192
192
  end
193
-
193
+
194
194
  attr_writer :bottom_right
195
195
  attr_writer :top_left
196
-
196
+
197
197
  attr_reader :index
198
198
  def index=( other )
199
199
  self.top_left = ModelIndexProxy.new( other )
@@ -11,20 +11,20 @@ the matching record next after this.
11
11
  class TableSearcher
12
12
  include OrderedDataset
13
13
  attr_reader :search_criteria, :field
14
-
14
+
15
15
  # dataset is a Sequel::Dataset, which has an associated Sequel::Model
16
16
  # field is an instance of Clevic::Field
17
17
  # search_criteria responds to from_start?, direction, whole_words? and search_text
18
18
  def initialize( dataset, search_criteria, field )
19
19
  raise "field must be specified" if field.nil?
20
20
  raise "unknown order #{search_criteria.direction}" unless [:forwards, :backwards].include?( search_criteria.direction )
21
-
21
+
22
22
  self.dataset = dataset
23
-
23
+
24
24
  @search_criteria = search_criteria
25
25
  @field = field
26
26
  end
27
-
27
+
28
28
  # start_entity is the entity to start from, ie any record found after it will qualify
29
29
  # return the first entity found that matches the criteria
30
30
  def search( start_entity = nil )
@@ -36,12 +36,12 @@ protected
36
36
  def search_field_expression
37
37
  if field.association?
38
38
  raise "display not specified for #{field}" if field.display.nil?
39
-
39
+
40
40
  # for related table displays as procs
41
41
  unless [String,Symbol].include?( field.display.class )
42
42
  raise( "search field #{field.inspect} cannot search lambda display" )
43
43
  end
44
-
44
+
45
45
  # TODO this will only work with a path value with no dots
46
46
  # otherwise the SQL gets complicated with joins etc
47
47
  field.related_class \
@@ -52,7 +52,7 @@ protected
52
52
  field.attribute.to_sym
53
53
  end
54
54
  end
55
-
55
+
56
56
  # return an expression, or an array or expressions for representing search_criteria.search_text and whole_words?
57
57
  def search_text_expression
58
58
  if search_criteria.whole_words?
@@ -66,7 +66,7 @@ protected
66
66
  "%#{search_criteria.search_text}%"
67
67
  end
68
68
  end
69
-
69
+
70
70
  # Add the relevant conditions to use start_entity as the
71
71
  # entity where the search starts, ie the first one after it is found
72
72
  # start_entity is a model instance
@@ -76,12 +76,12 @@ protected
76
76
  # pure boolean expression - they need something to compare it to.
77
77
  dataset.filter( expression => true )
78
78
  end
79
-
79
+
80
80
  # return a dataset based on dataset which filters on search_criteria
81
81
  def search_dataset( start_entity )
82
82
  likes = Array[*search_text_expression].map{|ste| Sequel::SQL::StringExpression.like(search_field_expression, ste, {:case_insensitive=>true})}
83
83
  rv = dataset.filter( Sequel::SQL::BooleanExpression.new(:OR, *likes ) )
84
-
84
+
85
85
  # if we're not searching from the start, we need
86
86
  # to find the next match. Which is complicated from an SQL point of view.
87
87
  unless search_criteria.from_start?
@@ -89,25 +89,25 @@ protected
89
89
  # build up the ordering conditions
90
90
  rv = find_from( rv, start_entity )
91
91
  end
92
-
92
+
93
93
  # reverse order by direction if necessary
94
94
  rv = rv.reverse if search_criteria.direction == :backwards
95
-
95
+
96
96
  # return dataset
97
97
  rv
98
98
  end
99
-
99
+
100
100
  # recursively create a case statement to do the comparison
101
101
  # because and ... and ... and filters on *each* one rather than
102
102
  # consecutively.
103
103
  def build_recursive_comparison( start_entity, index = 0 )
104
104
  # end recursion
105
105
  return false if index == order_attributes.size
106
-
106
+
107
107
  # fetch the current order attribute and direction
108
108
  attribute, direction = order_attributes[index]
109
109
  value = start_entity.send( attribute )
110
-
110
+
111
111
  # build case statement using Sequel expressions, including recursion
112
112
  # pseudo-SQL is
113
113
  # case
@@ -115,7 +115,7 @@ protected
115
115
  # when attribute = value then #{build_recursive_comparison( operator, index+1 )}
116
116
  # else false
117
117
  # end
118
-
118
+
119
119
  {
120
120
  # if values are unequal, comparison levels end here
121
121
  attribute.identifier.send( comparator(direction), value ) => true,
@@ -123,7 +123,7 @@ protected
123
123
  { attribute => value } => build_recursive_comparison( start_entity, index+1 )
124
124
  }.case( false ) # the else (default) clause, ie we don't want to see these records
125
125
  end
126
-
126
+
127
127
  # return either > or < depending on both search_criteria.direction
128
128
  # and local_direction
129
129
  def comparator( local_direction = 1 )
@@ -132,11 +132,11 @@ protected
132
132
  when :forwards; 1
133
133
  when :backwards; -1
134
134
  end * local_direction
135
-
135
+
136
136
  # 1 indexes >, -1 indexes <
137
137
  ['','>','<'][comparator_direction]
138
138
  end
139
-
139
+
140
140
  end
141
141
 
142
142
  end