clevic 0.13.0.b3 → 0.13.0.b5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/History.txt +21 -0
  2. data/Manifest.txt +91 -85
  3. data/README.txt +33 -18
  4. data/Rakefile +2 -3
  5. data/TODO +8 -14
  6. data/bin/clevic +18 -20
  7. data/lib/clevic.rb +7 -1
  8. data/lib/clevic/action_builder.rb +4 -1
  9. data/lib/clevic/ar_methods.rb +72 -57
  10. data/lib/clevic/attribute_list.rb +4 -0
  11. data/lib/clevic/cache_table.rb +43 -69
  12. data/lib/clevic/dataset_roller.rb +22 -0
  13. data/lib/clevic/delegate.rb +11 -5
  14. data/lib/clevic/delegates/combo_delegate.rb +156 -0
  15. data/lib/clevic/delegates/distinct_delegate.rb +48 -0
  16. data/lib/clevic/delegates/relational_delegate.rb +59 -0
  17. data/lib/clevic/delegates/set_delegate.rb +31 -0
  18. data/lib/clevic/field.rb +39 -55
  19. data/lib/clevic/field_valuer.rb +23 -10
  20. data/lib/clevic/filter_command.rb +22 -36
  21. data/lib/clevic/framework.rb +37 -0
  22. data/lib/clevic/generic_format.rb +5 -1
  23. data/lib/clevic/many_field.rb +28 -3
  24. data/lib/clevic/model_builder.rb +27 -32
  25. data/lib/clevic/ordered_dataset.rb +45 -0
  26. data/lib/clevic/qt.rb +4 -1
  27. data/lib/clevic/qt/action_builder.rb +9 -1
  28. data/lib/clevic/qt/browser.rb +1 -1
  29. data/lib/clevic/qt/clipboard.rb +3 -3
  30. data/lib/clevic/qt/combo_delegate.rb +25 -89
  31. data/lib/clevic/qt/delegate.rb +25 -0
  32. data/lib/clevic/qt/distinct_delegate.rb +5 -23
  33. data/lib/clevic/qt/extensions.rb +8 -1
  34. data/lib/clevic/qt/qt_combo_box.rb +58 -0
  35. data/lib/clevic/qt/relational_delegate.rb +18 -58
  36. data/lib/clevic/qt/set_delegate.rb +4 -34
  37. data/lib/clevic/qt/simplest_delegate.rb +19 -0
  38. data/lib/clevic/qt/table_model.rb +10 -10
  39. data/lib/clevic/qt/table_view.rb +7 -23
  40. data/lib/clevic/qt/text_delegate.rb +2 -2
  41. data/lib/clevic/qt/ui/browser_ui.rb +1 -1
  42. data/lib/clevic/qt/ui/search_dialog_ui.rb +1 -1
  43. data/lib/clevic/rails_models_loaders.rb +13 -0
  44. data/lib/clevic/record.rb +2 -2
  45. data/lib/clevic/sampler.rb +85 -39
  46. data/lib/clevic/sequel_ar_adapter.rb +1 -28
  47. data/lib/clevic/sequel_clevic.rb +68 -0
  48. data/lib/clevic/sequel_meta.rb +1 -13
  49. data/lib/clevic/subclasses.rb +18 -0
  50. data/lib/clevic/swing.rb +2 -1
  51. data/lib/clevic/swing/action.rb +27 -3
  52. data/lib/clevic/swing/action_builder.rb +0 -2
  53. data/lib/clevic/swing/browser.rb +1 -10
  54. data/lib/clevic/swing/combo_delegate.rb +45 -133
  55. data/lib/clevic/swing/delegate.rb +2 -0
  56. data/lib/clevic/swing/distinct_delegate.rb +2 -14
  57. data/lib/clevic/swing/relational_delegate.rb +2 -20
  58. data/lib/clevic/swing/set_delegate.rb +13 -28
  59. data/lib/clevic/swing/table_view.rb +1 -1
  60. data/lib/clevic/table_model.rb +3 -4
  61. data/lib/clevic/table_searcher.rb +10 -31
  62. data/lib/clevic/table_view.rb +97 -43
  63. data/lib/clevic/ui/browser_ui.rb +133 -0
  64. data/lib/clevic/ui/search_dialog_ui.rb +106 -0
  65. data/lib/clevic/version.rb +2 -2
  66. data/models/accounts_models.rb +24 -21
  67. data/models/times_models.rb +34 -28
  68. data/models/times_psql_models.rb +9 -3
  69. data/models/times_sqlite_models.rb +24 -1
  70. data/sql/times_sqlite.sql +3 -3
  71. data/tasks/clevic.rake +2 -2
  72. data/test/test_cache_table.rb +9 -19
  73. data/test/test_table_searcher.rb +2 -5
  74. metadata +95 -91
  75. data/lib/clevic/order_attribute.rb +0 -63
  76. data/lib/clevic/qt/boolean_delegate.rb +0 -8
  77. data/lib/clevic/qt/delegates.rb +0 -1
  78. data/lib/clevic/qt/item_delegate.rb +0 -66
  79. data/lib/clevic/sql_dialects.rb +0 -33
  80. data/tasks/website.rake +0 -25
  81. data/test/test_order_attribute.rb +0 -62
  82. data/test/test_sql_dialects.rb +0 -77
@@ -1,7 +1,7 @@
1
1
  =begin
2
2
  ** Form generated from reading ui file 'search_dialog.ui'
3
3
  **
4
- ** Created: Mon Jan 24 21:13:34 2011
4
+ ** Created: Mon Jan 31 16:10:23 2011
5
5
  ** by: Qt User Interface Compiler version 4.5.1
6
6
  **
7
7
  ** WARNING! All changes made in this file will be lost when recompiling ui file!
@@ -1,5 +1,18 @@
1
1
  require 'active_support'
2
2
 
3
+ def subclasses( base )
4
+ classes = []
5
+ ObjectSpace.each_object( Class ) do |x|
6
+ if x.ancestors.include?( base )
7
+ case
8
+ when x == Clevic.base_entity_class; # don't include this
9
+ else; classes << x
10
+ end
11
+ end
12
+ end
13
+ classes.sort{|a,b| a.name <=> b.name}
14
+ end
15
+
3
16
  def load_rails_models( root, config, models )
4
17
  # initialize Rails
5
18
  load config / 'environment.rb'
data/lib/clevic/record.rb CHANGED
@@ -30,11 +30,11 @@ module Clevic
30
30
  # keep track of the order in which views are
31
31
  # defined, so that can be used as the default ordering
32
32
  # of the views.
33
- Clevic::View.order << default_view_class_name.constantize
33
+ Clevic::View.order << eval( default_view_class_name )
34
34
  end
35
35
 
36
36
  def default_view_class
37
- @default_view_class ||= eval default_view_class_name
37
+ @default_view_class ||= eval( default_view_class_name )
38
38
  end
39
39
 
40
40
  # Need to defer the execution of the view definition block
@@ -3,65 +3,98 @@ require 'andand'
3
3
 
4
4
  module Clevic
5
5
 
6
- # Calculate a string sample for a particular Field
6
+ # This is used as part of the process of calculating the width
7
+ # of a field in the UI. Since the font is important, this computes
8
+ # a string value for a field that can be given to the font metrics
9
+ # for a framework. Uses various heuristics to compute the string
10
+ # values for different kinds of fields.
7
11
  class Sampler
8
- def initialize( entity_class, field_name, display, &block )
9
- @entity_class = entity_class
10
- @field_name = field_name
11
- @display = display
12
- @format_block = block
12
+ # display is only used for relational fields
13
+ def initialize( field, &format_block )
14
+ @field = field
15
+ @format_block = format_block
13
16
  end
14
-
15
- attr_reader :entity_class, :field_name, :display
16
-
17
+ attr_reader :field
18
+
19
+ def entity_class
20
+ field.entity_class
21
+ end
22
+
23
+ def field_name
24
+ field.attribute
25
+ end
26
+
27
+ def display
28
+ field.display
29
+ end
30
+
17
31
  def meta
18
- @meta ||= entity_class.meta[field_name]
32
+ field.meta
19
33
  end
20
-
34
+
21
35
  # return a string which is representative of the width of the field
22
36
  def compute
23
- case meta.type
24
- when :string, :text
25
- string_sample
26
-
27
- when :date, :time, :datetime, :timestamp
28
- date_time_sample
29
-
30
- when :numeric, :decimal, :integer, :float
31
- numeric_sample
32
-
33
- # TODO return a width, or something like that
34
- when :boolean; 'W'
35
-
36
- when :many_to_one
37
- related_sample
38
-
37
+ if field.set
38
+ # choose the longest value in the set
39
+ set = field.set_for( entity_class.first )
40
+ if set.is_a?( Hash )
41
+ set.values
42
+ else
43
+ set
44
+ end. \
45
+ max{|a,b| a.to_s.length <=> b.to_s.length }.upcase
39
46
  else
40
- if meta.type != NilClass
41
- raise "Sampler#compute can't figure out sample for #{entity_class.name}.#{field_name} because it's a #{meta.type.inspect}"
47
+ # choose samples based on the type of the field
48
+ case meta.type
49
+ when :boolean
50
+ field.label
51
+
52
+ when :string, :text
53
+ string_sample
54
+
55
+ when :date, :time, :datetime, :timestamp
56
+ date_time_sample
57
+
58
+ when :numeric, :decimal, :integer, :float
59
+ numeric_sample
60
+
61
+ # TODO return a width, or something like that
62
+ when :boolean; 'W'
63
+
64
+ when :many_to_one
65
+ related_sample
66
+
67
+ else
68
+ if meta.type != NilClass
69
+ raise "Sampler#compute can't figure out sample for #{entity_class.name}.#{field_name} because it's a #{meta.type.inspect}"
70
+ end
42
71
  end
43
-
44
72
  end
45
73
  end
46
-
74
+
47
75
  def do_format( value )
48
76
  @format_block.call( value )
49
77
  end
50
-
78
+
51
79
  # default to max length of 20
52
80
  def string_sample
53
81
  'N' * ( entity_class.max( :length.sql_function( field_name ) ).andand.to_i || 20 )
54
82
  end
55
83
 
56
- def date_time_sample
57
- sample_date = entity_class \
84
+ def sample_date_time
85
+ ds = entity_class \
58
86
  .filter( ~{ field_name => nil } ) \
59
87
  .select( field_name ) \
60
- .limit(1) \
61
- .single_value
62
- ;
88
+ .limit(1)
89
+ # can't use single-value here because the typecast_on_load
90
+ # isn't called unless we access the value via the entity object
91
+ ds.first.send( field_name )
92
+ end
93
+
94
+ def date_time_sample
63
95
  # replace all letters with 'N'
64
- do_format( sample_date || Date.today ).gsub( /[[:alpha:]]/, 'N' )
96
+ # and numbers with 8
97
+ do_format( sample_date_time || Date.today ).andand.gsub( /[[:alpha:]]/, 'N' ).gsub( /\d/, '8' )
65
98
  end
66
99
 
67
100
  def numeric_sample
@@ -71,9 +104,22 @@ class Sampler
71
104
  '9' * ( max_length || 5 )
72
105
  end
73
106
 
107
+ # Hmm. The first reified exemplar of a relational nested Field
108
+ class VirtualField
109
+ def initialize( entity_class, display )
110
+ @entity_class, @display = entity_class, display
111
+ end
112
+
113
+ def entity_class; @entity_class; end
114
+ def attribute; @display; end
115
+ def display; nil; end
116
+ def meta; @entity_class.meta[attribute]; end
117
+ def set; nil; end
118
+ end
119
+
74
120
  def related_sample
75
121
  if display.respond_to?( :to_sym )
76
- Sampler.new( eval( meta.class_name ), display.to_sym, nil, &@format_block ).compute
122
+ Sampler.new( VirtualField.new( eval( meta.class_name ), display.to_sym ), &@format_block ).compute
77
123
  end
78
124
  end
79
125
  end
@@ -123,6 +123,7 @@ end
123
123
  module Sequel
124
124
  class Model
125
125
  class << self
126
+ # for translating class methods for relations
126
127
  def translate_options( options )
127
128
  options[:key] = options[:foreign_key].andand.to_sym
128
129
  options.delete( :foreign_key )
@@ -150,18 +151,6 @@ module Sequel
150
151
  end
151
152
  end
152
153
 
153
- def table_exists?
154
- db.table_exists?( implicit_table_name )
155
- end
156
-
157
- def column_names
158
- columns
159
- end
160
-
161
- def reflections
162
- association_reflections
163
- end
164
-
165
154
  def has_attribute?( attribute )
166
155
  column_names.include?( attribute )
167
156
  end
@@ -182,22 +171,6 @@ module Sequel
182
171
  end
183
172
  end
184
173
 
185
- def readonly?
186
- false
187
- end
188
-
189
- def changed?
190
- modified?
191
- end
192
-
193
- def new_record?; new?; end
194
-
195
- class Errors
196
- def invalid?( field_name )
197
- self.has_key?( field_name )
198
- end
199
- end
200
-
201
174
  module Associations
202
175
  class ManyToOneAssociationReflection
203
176
  # return class for this side of the association
@@ -0,0 +1,68 @@
1
+ require 'sequel'
2
+
3
+ module Sequel
4
+ module Plugins
5
+ # This is used by Clevic to talk to models. It's here because
6
+ # I'd rather keep some kind of layer in case it's necessary
7
+ # to become more pluggable in relation to ORM frameworks.
8
+ module Clevic
9
+ def self.configure(model, options = {})
10
+ model.instance_eval do
11
+ # store model-related stuff here
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ # Copy the necessary class instance variables to the subclass.
17
+ def inherited(subclass)
18
+ super
19
+ end
20
+
21
+ # This doesn't really belong here, but I don't want to make
22
+ # a whole new plugin.
23
+ def table_exists?
24
+ db.table_exists?( implicit_table_name )
25
+ end
26
+
27
+ # Hmm, maybe these need to go in a different plugin
28
+ def column_names
29
+ columns
30
+ end
31
+
32
+ # Getting heavy enough, yet?
33
+ def reflections
34
+ association_reflections
35
+ end
36
+
37
+ end
38
+
39
+ module InstanceMethods
40
+ # This should also go in another plugin
41
+ def changed?
42
+ modified?
43
+ end
44
+
45
+ def readonly?
46
+ false
47
+ end
48
+
49
+ def new_record?
50
+ new?
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Sequel::Model.plugin :clevic
58
+
59
+ # This doesn't seem to work inside the plugin
60
+ module Sequel
61
+ class Model
62
+ class Errors
63
+ def invalid?( field_name )
64
+ self.has_key?( field_name )
65
+ end
66
+ end
67
+ end
68
+ end
@@ -15,19 +15,6 @@ module Sequel
15
15
  end
16
16
 
17
17
  module ClassMethods
18
- # Copy the necessary class instance variables to the subclass.
19
- def inherited(subclass)
20
- super
21
- #~ store = @cache_store
22
- #~ ttl = @cache_ttl
23
- #~ cache_ignore_exceptions = @cache_ignore_exceptions
24
- #~ subclass.instance_eval do
25
- #~ @cache_store = store
26
- #~ @cache_ttl = ttl
27
- #~ @cache_ignore_exceptions = cache_ignore_exceptions
28
- #~ end
29
- end
30
-
31
18
  def meta
32
19
  if @meta.nil?
33
20
  @meta = {}
@@ -54,6 +41,7 @@ module Sequel
54
41
  meta.values.map( &:keys ).include?( [ column ] )
55
42
  end
56
43
  end
44
+
57
45
  end
58
46
 
59
47
  module InstanceMethods
@@ -0,0 +1,18 @@
1
+ # copied from http://snippets.dzone.com/posts/show/2992
2
+ class Object
3
+ def self.subclasses(direct = false)
4
+ classes = []
5
+ if direct
6
+ ObjectSpace.each_object(Class) do |c|
7
+ next unless c.superclass == self
8
+ classes << c
9
+ end
10
+ else
11
+ ObjectSpace.each_object(Class) do |c|
12
+ next unless c.ancestors.include?(self) and (c != self)
13
+ classes << c
14
+ end
15
+ end
16
+ classes
17
+ end
18
+ end
data/lib/clevic/swing.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  Require this file to do Clevic in Swing with JRuby
3
3
  =end
4
4
 
5
- require 'pathname'
5
+ require 'clevic/framework'
6
6
 
7
7
  # This seems to be required for jruby-1.5.x (at least for 1.5.2)
8
8
  require 'java'
@@ -12,6 +12,7 @@ require 'clevic/swing/table_model.rb'
12
12
  require 'clevic/swing/table_view.rb'
13
13
 
14
14
  # all other files in the swing subdirectory
15
+ require 'pathname'
15
16
  ( Pathname.new( __FILE__ ).parent + 'swing' ).children.grep( /.rb$/ ).each do |child|
16
17
  require child.to_s
17
18
  end
@@ -4,10 +4,22 @@ module Clevic
4
4
 
5
5
  class Action
6
6
  include Gather
7
- property :shortcut, :method, :handler, :tool_tip, :visible, :name, :text, :checkable
7
+ property :shortcut, :method, :handler, :tool_tip, :visible
8
+ property :name, :text, :checkable, :enabled
9
+
10
+ # Needed to enable / disable accelerators on the fly.
11
+ def enabled=( bool )
12
+ # test for @menu_item instead of the method to
13
+ # work around Swing Stupidity. See comments in menu_item.
14
+ menu_item.enabled = bool unless @menu_item.nil?
15
+ @enabled = bool
16
+ end
8
17
 
9
18
  def initialize( parent, options = {}, &block )
10
19
  @parent = parent
20
+ @enabled = true
21
+
22
+ # work around the Swing Stupidity detailed in enabled=
11
23
  gather( options, &block )
12
24
  end
13
25
  attr_reader :parent, :menu_item
@@ -16,6 +28,10 @@ class Action
16
28
  text.gsub( /&/, '' )
17
29
  end
18
30
 
31
+ def object_name
32
+ name.to_s
33
+ end
34
+
19
35
  # find the java.awt.event.KeyEvent::VK constant
20
36
  # for the letter after the &. Then set it on the item's
21
37
  # mnemonic. Because JMenuItem.setMnemonic won't take a nil
@@ -41,6 +57,12 @@ class Action
41
57
  javax.swing.JMenuItem.new
42
58
  end
43
59
 
60
+ # Menu item always enabled, until later.
61
+ # Otherwise it prevents the assignment
62
+ # of an accelerator key. So we have to
63
+ # work around yet another Swing stupidity.
64
+ menu_item.enabled = true
65
+
44
66
  menu_item.text = plain_text
45
67
  menu_item.mnemonic = mnemonic if mnemonic
46
68
  menu_item.accelerator = parse_shortcut( shortcut ) unless shortcut.nil?
@@ -48,6 +70,10 @@ class Action
48
70
  menu_item.add_action_listener do |event|
49
71
  handler.call( event )
50
72
  end
73
+
74
+ # Put this at the end so it doesn't interfere with the
75
+ # keystroke assignment Swing Stupidity.
76
+ menu_item.enabled = enabled
51
77
  end
52
78
  @menu_item
53
79
  end
@@ -92,7 +118,6 @@ class Action
92
118
 
93
119
  else
94
120
  keystring = javax.swing.KeyStroke.getKeyStroke( java.lang.Character.new( last.to_char ), modifier_mask ).toString
95
- puts "keystring: #{keystring.inspect}"
96
121
  # have to do this conversion for Mac OS X
97
122
  javax.swing.KeyStroke.getKeyStroke( keystring.gsub( /typed/, 'pressed' ) )
98
123
  end
@@ -103,7 +128,6 @@ class Action
103
128
  raise "too many options for #{sequence}: #{found.inspect}" if found.size != 1
104
129
  javax.swing.KeyStroke.getKeyStroke( eval( "java.awt.event.KeyEvent::#{found.first}" ), modifier_mask )
105
130
  end
106
- puts "keystroke: #{keystroke.inspect}"
107
131
  keystroke || raise( "unknown keystroke #{sequence} => #{modifiers.inspect} #{last}" )
108
132
  end
109
133
  end