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
@@ -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