clevic 0.13.0.b3 → 0.13.0.b5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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