clevic 0.13.0.b9 → 0.13.0.b10

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -3,21 +3,21 @@ module Clevic
3
3
  # Used for getting values from the entity based on the definitions
4
4
  # in the field.
5
5
  module FieldValuer
6
-
6
+
7
7
  # the value for this index
8
8
  # used to be gui_value, but that wasn't right
9
9
  def raw_value
10
10
  field.value_for( entity )
11
11
  end
12
-
12
+
13
13
  def display_value
14
14
  field.do_format( raw_value ) unless raw_value.nil?
15
15
  end
16
-
16
+
17
17
  def edit_value
18
18
  field.do_edit_format( raw_value ) unless raw_value.nil?
19
19
  end
20
-
20
+
21
21
  # Set the value from an editable text representation
22
22
  # of the value
23
23
  def edit_value=( value )
@@ -32,13 +32,13 @@ module Clevic
32
32
  when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{1,2})[ /-]?(\w{3})$}
33
33
  today = Date.today
34
34
  date = Date.parse( "#$1 #$2 #{Time.now.year.to_s}" )
35
-
35
+
36
36
  # change year to last year
37
37
  if (1..2).include?( today.month ) and (8..12).include?( date.month )
38
38
  date = Date.civil( Date.today.year - 1, date.month, date.day )
39
39
  end
40
40
  date
41
-
41
+
42
42
  # if a digit only is entered, fetch month and year from
43
43
  # previous row
44
44
  when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{1,2})$}
@@ -49,19 +49,19 @@ module Clevic
49
49
  else
50
50
  value
51
51
  end
52
-
52
+
53
53
  # Mostly to fix date strings that have come
54
54
  # out of the db and been formatted
55
55
  # Accepts dd mmm yy.
56
56
  # Assumes 20 as the century.
57
57
  when [:date,:datetime].include?( field.meta.type ) && value =~ %r{^(\d{2})[ /-](\w{3})[ /-](\d{2})$}
58
58
  Date.parse( "#$1 #$2 20#$3" )
59
-
59
+
60
60
  # allow lots of flexibility in entering times
61
61
  # 01:17, 0117, 117, 1 17, are all accepted
62
62
  when field.meta.type == :time && value =~ %r{^(\d{1,2}).?(\d{2})$}
63
63
  Time.parse( "#$1:#$2" )
64
-
64
+
65
65
  # remove thousand separators, allow for space and comma
66
66
  # instead of . as a decimal separator
67
67
  when field.meta.type == :decimal
@@ -74,12 +74,12 @@ module Clevic
74
74
  else
75
75
  value
76
76
  end.gsub( ',', '' ) # strip remaining commas
77
-
77
+
78
78
  else
79
79
  value
80
80
  end
81
81
  end
82
-
82
+
83
83
  def find_related( attribute, value )
84
84
  candidates = field.related_class.filter( attribute.to_sym => value ).all
85
85
  if candidates.size != 1
@@ -87,7 +87,7 @@ module Clevic
87
87
  end
88
88
  candidates.first
89
89
  end
90
-
90
+
91
91
  # set the field value from a value that could be
92
92
  # a text representation a-la edit_value, or possible
93
93
  # a name from a related entity
@@ -96,10 +96,10 @@ module Clevic
96
96
  when Symbol
97
97
  # we have a related class of some kind,
98
98
  self.attribute_value = find_related( field.display, value )
99
-
99
+
100
100
  when NilClass
101
101
  self.edit_value = value
102
-
102
+
103
103
  when String
104
104
  # allow plain strings, but not dotted paths
105
105
  if field.display['.']
@@ -107,12 +107,12 @@ module Clevic
107
107
  else
108
108
  self.attribute_value = find_related( field.display.to_sym, value )
109
109
  end
110
-
110
+
111
111
  else
112
112
  raise "display is a not a symbol or nil: #{display.inspect}"
113
113
  end
114
114
  end
115
-
115
+
116
116
  # fetch the value of the attribute, without following
117
117
  # the full path. This will return a related entity for
118
118
  # belongs_to or has_one relationships, or a plain value
@@ -120,39 +120,39 @@ module Clevic
120
120
  def attribute_value
121
121
  entity.send( field.attribute )
122
122
  end
123
-
123
+
124
124
  # cache the writer method name since it's not likely to change
125
125
  def writer
126
126
  @writer ||= "#{field.attribute.to_s}="
127
127
  end
128
-
128
+
129
129
  # set the value of the attribute, without following the
130
130
  # full path.
131
131
  # TODO remove need to constantly recalculate the attribute writer
132
132
  def attribute_value=( value )
133
133
  entity.send( writer, value )
134
134
  end
135
-
135
+
136
136
  def tooltip
137
137
  case
138
138
  # show validation errors
139
139
  when has_errors?
140
140
  errors.join("\n")
141
-
141
+
142
142
  # provide a tooltip when an empty relational field is encountered
143
143
  # TODO should be part of field definition
144
144
  when field.meta.type == :association
145
145
  field.delegate.if_empty_message
146
-
146
+
147
147
  # read-only field
148
148
  when field.read_only?
149
149
  field.tooltip_for( entity ) || 'Read-only'
150
-
150
+
151
151
  else
152
152
  field.tooltip_for( entity )
153
153
  end
154
154
  end
155
-
155
+
156
156
  class Valuer
157
157
  include FieldValuer
158
158
  attr_accessor :field, :entity
@@ -160,16 +160,16 @@ module Clevic
160
160
  @field, @entity = field, entity
161
161
  end
162
162
  end
163
-
163
+
164
164
  def valuer( entity )
165
165
  Valuer.new(field,entity)
166
166
  end
167
-
167
+
168
168
  def self.valuer( field, entity )
169
169
  Valuer.new(field,entity)
170
170
  end
171
171
  end
172
-
172
+
173
173
  # Provides the minimum necessary for valuing, ie for irb testing
174
174
  module SimpleFieldValuer
175
175
  include FieldValuer
@@ -7,17 +7,17 @@ module Clevic
7
7
  @message = message || 'filtered'
8
8
  @filter_block = filter_block
9
9
  end
10
-
10
+
11
11
  attr_reader :message
12
-
12
+
13
13
  # Do the filtering. Return true if successful, false otherwise.
14
14
  def doit
15
15
  # store current dataset
16
16
  @previous_dataset = @table_view.model.cache_table.dataset
17
-
17
+
18
18
  # store auto_new
19
19
  @auto_new = @table_view.model.auto_new
20
-
20
+
21
21
  # reload cache table with new conditions
22
22
  @table_view.model.auto_new = false
23
23
  @table_view.model.reload_data( &@filter_block )
@@ -28,11 +28,11 @@ module Clevic
28
28
  puts e.backtrace
29
29
  false
30
30
  end
31
-
31
+
32
32
  def undo
33
33
  # restore auto_new
34
34
  @table_view.model.auto_new = @auto_new
35
-
35
+
36
36
  # reload cache table with stored AR conditions
37
37
  @table_view.model.reload_data( @previous_dataset )
38
38
  end
@@ -12,7 +12,7 @@
12
12
  =end
13
13
 
14
14
  class Class
15
-
15
+
16
16
  # method_name can be a symbol or a string.
17
17
  #
18
18
  # If a method of this name doesn't already exist
@@ -26,7 +26,7 @@ class Class
26
26
  end
27
27
  end
28
28
  end
29
-
29
+
30
30
  def subclass_responsibility( method_name )
31
31
  unless instance_methods.include?( method_name.to_s )
32
32
  define_method( method_name ) do
@@ -34,5 +34,5 @@ class Class
34
34
  end
35
35
  end
36
36
  end
37
-
37
+
38
38
  end
@@ -22,7 +22,7 @@ module GenericFormat
22
22
  end
23
23
  end
24
24
  end
25
-
25
+
26
26
  # apply format to value. Use strftime for date_time types, or % for everything else.
27
27
  # If format is a proc, pass value to it.
28
28
  def do_generic_format( format, value )
@@ -50,7 +50,7 @@ module GenericFormat
50
50
  nil
51
51
  end
52
52
  end
53
-
53
+
54
54
  end
55
55
 
56
56
  end
@@ -13,15 +13,15 @@ module Clevic
13
13
  end
14
14
  end
15
15
  end
16
-
16
+
17
17
  def many_builder
18
18
  @many_view.builder
19
19
  end
20
-
20
+
21
21
  def many_fields
22
22
  many_builder.fields
23
23
  end
24
-
24
+
25
25
  # return an instance of Clevic::View that represents the many items
26
26
  # for this field
27
27
  def many_view( &block )
@@ -14,18 +14,18 @@ module Clevic
14
14
 
15
15
  == View definition
16
16
 
17
- Clevic::ModelBuilder defines the DSL used to create a UI definition (which is
18
- actually a set of Clevic::Field instances), including any related tables,
17
+ Clevic::ModelBuilder defines the DSL used to create a UI definition (which is
18
+ actually a set of Clevic::Field instances), including any related tables,
19
19
  restrictions on data entry, formatting and so on. The intention was to make
20
20
  specifying a UI as painless as possible, with framework overhead only where
21
21
  you need it.
22
22
 
23
23
  To that end, there are 2 ways to define UIs:
24
24
 
25
- - an Embedded View as part of the model (Sequel::Model) object (which is useful if you
25
+ - an Embedded View as part of the model (Sequel::Model) object (which is useful if you
26
26
  want minimal framework overhead). Just show me the data, dammit.
27
27
 
28
- - a Separate View in a separate class (which is useful when you want several
28
+ - a Separate View in a separate class (which is useful when you want several
29
29
  diffent views of the same underlying table). I want a neato-nifty UI that does
30
30
  (relatively) complex things.
31
31
 
@@ -58,32 +58,32 @@ could be defined like this:
58
58
  belongs_to :invoice
59
59
  belongs_to :activity
60
60
  belongs_to :project
61
-
61
+
62
62
  include Clevic::Record
63
-
63
+
64
64
  # spans of time more than 8 ours are coloured violet
65
65
  # because they're often the result of typos.
66
66
  def time_color
67
67
  return if self.end.nil? || start.nil?
68
68
  'darkviolet' if self.end - start > 8.hours
69
69
  end
70
-
70
+
71
71
  # tooltip for spans of time > 8 hours
72
72
  def time_tooltip
73
73
  return if self.end.nil? || start.nil?
74
74
  'Time interval greater than 8 hours' if self.end - start > 8.hours
75
75
  end
76
-
76
+
77
77
  define_ui do
78
78
  plain :date, :sample => '28-Dec-08'
79
-
79
+
80
80
  # The project field
81
81
  relational :project do |field|
82
82
  field.display = 'project'
83
-
83
+
84
84
  # see Sequel::Dataset docs
85
85
  field.dataset.filter( :active => true ).order{ lower(project) }
86
-
86
+
87
87
  # handle data changed events. In this case,
88
88
  # auto-fill-in the invoice field.
89
89
  field.notify_data_changed do |entity_view, table_view, model_index|
@@ -95,29 +95,29 @@ could be defined like this:
95
95
  end
96
96
  end
97
97
  end
98
-
98
+
99
99
  relational :invoice, :display => 'invoice_number', :conditions => "status = 'not sent'", :order => 'invoice_number'
100
-
100
+
101
101
  # call time_color method for foreground color value
102
102
  plain :start, :foreground => :time_color, :tooltip => :time_tooltip
103
-
103
+
104
104
  # another way to call time_color method for foreground color value
105
105
  plain :end, :foreground => lambda{|x| x.time_color}, :tooltip => :time_tooltip
106
-
106
+
107
107
  # multiline text
108
108
  text :description, :sample => 'This is a long string designed to hold lots of data and description'
109
-
109
+
110
110
  relational :activity do
111
111
  display 'activity'
112
112
  order 'lower(activity)'
113
113
  sample 'Troubleshooting'
114
114
  conditions 'active = true'
115
115
  end
116
-
116
+
117
117
  distinct :module, :tooltip => 'Module or sub-project'
118
118
  plain :charge, :tooltip => 'Is this time billable?'
119
119
  distinct :person, :default => 'John', :tooltip => 'The person who did the work'
120
-
120
+
121
121
  records :order => 'date, start, id'
122
122
  end
123
123
 
@@ -125,11 +125,11 @@ could be defined like this:
125
125
  action_builder.action :smart_copy, 'Smart Copy', :shortcut => 'Ctrl+"' do
126
126
  smart_copy( view )
127
127
  end
128
-
128
+
129
129
  action_builder.action :invoice_from_project, 'Invoice from Project', :shortcut => 'Ctrl+Shift+I' do
130
130
  invoice_from_project( view, view.current_index ) do
131
131
  # execute the block if the invoice is changed
132
-
132
+
133
133
  # save this before selection model is cleared
134
134
  current_index = view.current_index
135
135
  view.selection_model.clear
@@ -137,35 +137,35 @@ could be defined like this:
137
137
  end
138
138
  end
139
139
  end
140
-
140
+
141
141
  # do a smart copy from the previous line
142
142
  def self.smart_copy( view )
143
143
  view.sanity_check_read_only
144
144
  view.sanity_check_ditto
145
-
145
+
146
146
  # need a reference to current_index here, because selection_model.clear will
147
147
  # invalidate view.current_index. And anyway, its shorter and easier to read.
148
148
  current_index = view.current_index
149
149
  if current_index.row >= 1
150
150
  # fetch previous item
151
151
  previous_item = view.model.collection[current_index.row - 1]
152
-
152
+
153
153
  # copy the relevant fields
154
154
  current_index.entity.date = previous_item.date if current_index.entity.date.blank?
155
155
  # depends on previous line
156
156
  current_index.entity.start = previous_item.end if current_index.entity.date == previous_item.date
157
-
157
+
158
158
  # copy rest of fields
159
159
  [:project, :invoice, :activity, :module, :charge, :person].each do |attr|
160
160
  current_index.entity.send( "#{attr.to_s}=", previous_item.send( attr ) )
161
161
  end
162
-
162
+
163
163
  # tell view to update
164
164
  view.model.data_changed do |change|
165
165
  change.top_left = current_index.choppy( :column => 0 )
166
166
  change.bottom_right = current_index.choppy( :column => view.model.fields.size - 1 )
167
167
  end
168
-
168
+
169
169
  # move to the first empty time field
170
170
  next_field =
171
171
  if current_index.entity.start.blank?
@@ -173,7 +173,7 @@ could be defined like this:
173
173
  else
174
174
  :end
175
175
  end
176
-
176
+
177
177
  # next cursor location
178
178
  view.selection_model.clear
179
179
  view.current_index = current_index.choppy( :column => next_field )
@@ -190,10 +190,10 @@ could be defined like this:
190
190
  unless invoice.nil?
191
191
  # make a reference to the invoice
192
192
  current_index.entity.invoice = invoice
193
-
193
+
194
194
  # update view from top_left to bottom_right
195
195
  table_view.model.data_changed( current_index.choppy( :column => :invoice ) )
196
-
196
+
197
197
  unless block.nil?
198
198
  if block.arity == 1
199
199
  block.call( invoice )
@@ -204,17 +204,17 @@ could be defined like this:
204
204
  end
205
205
  end
206
206
  end
207
-
207
+
208
208
  end
209
209
 
210
210
  == Separate View
211
211
 
212
212
  To define a separate ui class, do something like this:
213
213
  class Prospect < Clevic::View
214
-
214
+
215
215
  # This is the Sequel::Model descendant
216
216
  entity_class Position
217
-
217
+
218
218
  # This must return a ModelBuilder instance, which is made easier
219
219
  # by putting the block in a call to model_builder.
220
220
  #
@@ -226,15 +226,15 @@ To define a separate ui class, do something like this:
226
226
  model_builder do |mb|
227
227
  # use the define_ui block from Position
228
228
  mb.exec_ui_block( Position )
229
-
229
+
230
230
  # any other ModelBuilder code can go here too
231
-
231
+
232
232
  # use a different recordset
233
233
  mb.records :conditions => "status in ('prospect','open')", :order => 'date desc,code'
234
234
  end
235
235
  end
236
236
  end
237
-
237
+
238
238
  And you can even inherit UIs:
239
239
 
240
240
  class Extinct < Prospect
@@ -329,13 +329,13 @@ display, and can have optional keyboard shortcuts:
329
329
  # view.current_index.entity will return the entity instance.
330
330
  smart_copy( view )
331
331
  end
332
-
332
+
333
333
  action_builder.action :invoice_from_project, 'Invoice from Project', :shortcut => 'Ctrl+Shift+I' do
334
334
  # a method in the class containing define_actions
335
335
  invoice_from_project( view.current_index, view )
336
336
  end
337
337
  end
338
-
338
+
339
339
  === Notifications
340
340
 
341
341
  Key presses will be sent here:
@@ -368,7 +368,7 @@ the order can be accessed in Clevic::View.order, and specified by
368
368
  =end
369
369
 
370
370
  class ModelBuilder
371
-
371
+
372
372
  # Create a definition for entity_view (subclass of Clevic::View).
373
373
  # Then execute block using self.instance_eval.
374
374
  # entity_view must respond to entity_class, and if title is called, it
@@ -377,14 +377,13 @@ class ModelBuilder
377
377
  @entity_view = entity_view
378
378
  @auto_new = true
379
379
  @read_only = false
380
- # TODO not needed for 1.9
381
- @fields = OrderedHash.new
380
+ @fields = Hash.new
382
381
  exec_ui_block( &block )
383
382
  end
384
-
383
+
385
384
  attr_accessor :entity_view
386
385
  attr_accessor :find_options
387
-
386
+
388
387
  # execute a block containing method calls understood by Clevic::ModelBuilder
389
388
  # arg can be something that responds to define_ui_block,
390
389
  # or just the block will be executed. If both are present,
@@ -405,92 +404,96 @@ class ModelBuilder
405
404
  end
406
405
  self
407
406
  end
408
-
407
+
409
408
  # The collection of Clevic::Field instances where visible == true.
410
409
  # the visible may go away.
411
410
  def fields
412
411
  #~ @fields.reject{|id,field| !field.visible}
413
412
  @fields
414
413
  end
415
-
414
+
416
415
  # return the index of the named field in the collection of fields.
417
416
  def index( field_name_sym )
418
417
  retval = nil
419
418
  fields.each_with_index{|id,field,i| retval = i if field.attribute == field_name_sym.to_sym }
420
419
  retval
421
420
  end
422
-
421
+
423
422
  # The ORM class
424
423
  def entity_class
425
424
  @entity_view.entity_class
426
425
  end
427
-
426
+
428
427
  # set read_only to true
429
428
  def read_only!
430
429
  @read_only = true
431
430
  end
432
-
431
+
433
432
  # should this table automatically show a new blank record?
434
433
  def auto_new( bool )
435
434
  @auto_new = bool
436
435
  end
437
-
436
+
438
437
  # should this table automatically show a new blank record?
439
438
  def auto_new?; @auto_new; end
440
-
439
+
441
440
  # DSL for changing the title
442
441
  def title( value )
443
442
  entity_view.title = value
444
443
  end
445
444
 
445
+ def add_field( field )
446
+ fields[field.id || field.attribute] = field
447
+ end
448
+
446
449
  # an ordinary field, edited in place with a text box
447
450
  def plain( attribute, options = {}, &block )
448
451
  read_only_default!( attribute, options )
449
452
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
450
-
453
+
451
454
  # plain_delegate will be defined in a framework-specific file.
452
455
  # This is becoming a kind of poor man's inheritance. I don't
453
456
  # think I like that.
454
457
  field.delegate = plain_delegate( field )
455
- @fields[attribute] = field
458
+ add_field field
456
459
  end
457
-
460
+
458
461
  # an ordinary field like plain, except that a larger edit area can be used
459
462
  def text( attribute, options = {}, &block )
460
463
  read_only_default!( attribute, options )
461
464
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
462
465
  field.delegate = TextAreaDelegate.new( field )
463
- @fields[attribute] = field
466
+ add_field field
464
467
  end
465
-
468
+
466
469
  # Returns a Clevic::Field with a DistinctDelegate, in other words
467
470
  # a combo box containing all values for this field from the table.
468
471
  def distinct( attribute, options = {}, &block )
469
472
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
470
473
  field.delegate = DistinctDelegate.new( field )
471
- @fields[attribute] = field
474
+ add_field field
472
475
  end
473
-
476
+
474
477
  # a combo box with a set of supplied values
475
478
  def combo( attribute, options = {}, &block )
476
479
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
477
-
480
+
478
481
  # TODO this really belongs in a separate 'map' field?
479
482
  # or maybe put it in SetDelegate?
480
483
  if field.set.is_a? Hash
481
484
  field.format ||= lambda{|x| field.set[x]}
482
485
  end
483
-
486
+
484
487
  field.delegate = SetDelegate.new( field )
485
- @fields[attribute] = field
488
+ add_field field
486
489
  end
487
490
 
488
- # Returns a Clevic::Field with a restricted SetDelegate,
491
+ # Returns a Clevic::Field with a restricted SetDelegate,
489
492
  def restricted( attribute, options = {}, &block )
490
493
  options[:restricted] = true
491
494
  combo( attribute, options, &block )
492
495
  end
493
-
496
+
494
497
  # For many_to_one relationships.
495
498
  # Edited with a combo box using values from the specified
496
499
  # path on the foreign key model object
@@ -499,34 +502,35 @@ class ModelBuilder
499
502
  def relational( attribute, options = {}, &block )
500
503
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
501
504
  field.delegate = RelationalDelegate.new( field )
502
- @fields[attribute] = field
505
+ add_field field
503
506
  end
504
-
507
+
505
508
  def tags( attribute, options = {}, &block )
506
509
  field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
507
-
510
+
508
511
  # build a collection setter if necessary
509
512
  unless entity_class.instance_methods.include? "#{attribute}="
510
513
  raise NotImplementedError, "Need to build a collection setter for '#{attribute}='"
511
514
  end
512
-
515
+
513
516
  field.delegate = TagDelegate.new( field )
514
- @fields[attribute] = field
517
+ add_field field
515
518
  end
516
519
 
517
520
  # force a checkbox
518
521
  def check( attribute, options = {}, &block )
519
522
  read_only_default!( attribute, options )
520
- field = @fields[attribute] = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
523
+ field = Clevic::Field.new( attribute.to_sym, entity_class, options, &block )
521
524
  field.delegate = BooleanDelegate.new( field )
525
+ add_field field
522
526
  end
523
-
527
+
524
528
  # specify the dataset but just calling and chaining, thusly
525
529
  # dataset.order( :some_field ).filter( :active => true )
526
530
  def dataset
527
531
  @dataset_roller = DatasetRoller.new( entity_class.dataset )
528
532
  end
529
-
533
+
530
534
  def records( *args )
531
535
  puts "ModelBuilder#records is deprecated. Use ModelBuilder#dataset instead"
532
536
  require 'clevic/sequel_ar_adapter.rb'
@@ -565,7 +569,7 @@ class ModelBuilder
565
569
  # don't create an empty record, because sometimes there are
566
570
  # validations that will cause trouble
567
571
  auto_new false
568
-
572
+
569
573
  # build columns
570
574
  entity_class.attributes.each do |column,model_column|
571
575
  begin
@@ -591,12 +595,12 @@ class ModelBuilder
591
595
  end
592
596
  end
593
597
  end
594
-
598
+
595
599
  # return the named Clevic::Field object
596
600
  def field( attribute )
597
- @fields.find {|id,field| field.attribute == attribute }
601
+ fields.find {|id,field| field.attribute == attribute }
598
602
  end
599
-
603
+
600
604
  # This takes all the information collected
601
605
  # by the other methods, and returns a new TableModel
602
606
  # with the given parent (usually a TableView) as its parent.
@@ -607,49 +611,49 @@ class ModelBuilder
607
611
  @model = Clevic::TableModel.new
608
612
  @model.builder = self
609
613
  @model.entity_view = entity_view
610
- @model.fields = @fields.values
614
+ @model.fields = fields.values
611
615
  @model.read_only = @read_only
612
616
  @model.auto_new = auto_new?
613
-
617
+
614
618
  # set view name
615
619
  parent.object_name = @object_name if parent.respond_to? :object_name
616
-
620
+
617
621
  # set UI parent for all delegates
618
622
  # and model for each field
619
623
  fields.each do |id,field|
620
624
  field.delegate.parent = parent unless field.delegate.nil?
621
625
  field.model = @model
622
626
  end
623
-
627
+
624
628
  # the data
625
629
  @model.collection = create_cache_table
626
-
630
+
627
631
  @model
628
632
  end
629
-
633
+
630
634
  protected
631
635
 
632
636
  # set a sensible read-only value if it isn't already specified in options
633
637
  def read_only_default!( attribute, options )
634
638
  # sensible defaults for read-only-ness
635
- options[:read_only] ||=
639
+ options[:read_only] ||=
636
640
  case
637
641
  when options[:display].respond_to?( :call )
638
642
  # it's a Proc or a Method, so we can't set it
639
643
  true
640
-
644
+
641
645
  when entity_class.column_names.include?( options[:display].to_s )
642
646
  # it's a DB column, so it's not read only
643
647
  false
644
-
648
+
645
649
  when entity_class.reflections.include?( attribute )
646
650
  # one-to-one relationships can be edited. many-to-one certainly can't
647
651
  entity_class.meta[attribute].type != :many_to_one
648
-
652
+
649
653
  when entity_class.method_defined?( attribute )
650
654
  # read-only if there's no setter for the attribute
651
655
  !entity_class.method_defined?( "#{attribute.to_s}=" )
652
-
656
+
653
657
  else
654
658
  # default to not read-only
655
659
  false