clevic 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +12 -999
- data/Manifest.txt +1 -0
- data/README.txt +13 -10
- data/Rakefile +4 -3
- data/TODO +28 -13
- data/bin/clevic +64 -11
- data/config/hoe.rb +1 -1
- data/lib/clevic/browser.rb +86 -116
- data/lib/clevic/cache_table.rb +14 -4
- data/lib/clevic/db_options.rb +13 -8
- data/lib/clevic/delegates.rb +3 -1
- data/lib/clevic/extensions.rb +16 -0
- data/lib/clevic/field.rb +13 -3
- data/lib/clevic/field_builder.rb +42 -0
- data/lib/clevic/model_builder.rb +285 -92
- data/lib/clevic/record.rb +45 -2
- data/lib/clevic/table_model.rb +137 -40
- data/lib/clevic/table_view.rb +345 -150
- data/lib/clevic/ui/browser.ui +18 -73
- data/lib/clevic/ui/browser_ui.rb +57 -99
- data/lib/clevic/ui/search_dialog_ui.rb +51 -50
- data/lib/clevic/version.rb +1 -1
- data/models/accounts_models.rb +21 -26
- data/models/minimal_models.rb +2 -0
- data/models/times_models.rb +78 -79
- data/models/times_sqlite_models.rb +8 -8
- data/models/values_models.rb +7 -6
- data/website/index.html +24 -27
- metadata +4 -3
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -9,7 +9,8 @@ editing of tables in a pre-existing relational DBMS.
|
|
9
9
|
|
10
10
|
Using ActiveRecord means Clevic supports Postgresql, Mysql and so on. It's been tested with Postgres and sqlite.
|
11
11
|
|
12
|
-
Using Qt means it runs on Linux, Windows and OSX.
|
12
|
+
Using Qt means it runs on Linux, Windows and OSX. Thoroughly tested
|
13
|
+
in Linux, slightly tested in Windows and OSX.
|
13
14
|
|
14
15
|
== FEATURES:
|
15
16
|
|
@@ -22,6 +23,7 @@ Using Qt means it runs on Linux, Windows and OSX.
|
|
22
23
|
* Filter by current field.
|
23
24
|
* search by field contents.
|
24
25
|
* cut and paste in CSV format
|
26
|
+
* point Clevic at a Rails project and see your models in a GUI
|
25
27
|
|
26
28
|
=== Shortcuts:
|
27
29
|
|
@@ -37,9 +39,11 @@ Using Qt means it runs on Linux, Windows and OSX.
|
|
37
39
|
|
38
40
|
Models and their UI representation must be defined in Ruby. A class that
|
39
41
|
inherits from Clevic::Record (which itself inherits from ActiveRecord::Base) will provide
|
40
|
-
a minimally functional UI.
|
41
|
-
|
42
|
-
|
42
|
+
a minimally functional UI. Beyond that, the framework provides
|
43
|
+
a DSL for defining more complex and useful behaviour (see Clevic::ModelBuilder).
|
44
|
+
|
45
|
+
Clevic also knows how to build a default UI with sensible defaults from
|
46
|
+
ActiveRecord::Base subclasses.
|
43
47
|
|
44
48
|
In the models/ subdirectory, start with minimal_models.rb.
|
45
49
|
account_models.rb and times_models.rb provide definitions for more real-world examples.
|
@@ -57,18 +61,17 @@ comments, see Clevic::Browser and Clevic::ModelBuilder.
|
|
57
61
|
* leverages SQL whenever possible to handle large datasets, sorting, filtering
|
58
62
|
etc. So it's probably not suitable for talking to a remote db across a slow link.
|
59
63
|
|
60
|
-
=== Plans
|
61
|
-
|
62
|
-
* sortable by row headers
|
63
|
-
* color highlighting of fields and records on definable criteria
|
64
|
-
|
65
64
|
== PROBLEMS:
|
66
65
|
|
67
66
|
See TODO file.
|
68
67
|
|
69
68
|
== SYNOPSIS:
|
70
69
|
|
71
|
-
clevic model_definition_file
|
70
|
+
clevic model_definition_file.rb
|
71
|
+
|
72
|
+
OR
|
73
|
+
|
74
|
+
clevic path_to_rails_project [model_tweaks.rb]
|
72
75
|
|
73
76
|
== REQUIREMENTS:
|
74
77
|
|
data/Rakefile
CHANGED
@@ -72,6 +72,7 @@ end
|
|
72
72
|
desc "irb in this project's context"
|
73
73
|
task :irb do |t|
|
74
74
|
ARGV.shift()
|
75
|
+
ENV['RUBYLIB'] ||= ''
|
75
76
|
ENV['RUBYLIB'] += ":#{File.expand_path('.')}/lib"
|
76
77
|
exec "irb -Ilib -rclevic"
|
77
78
|
end
|
@@ -124,8 +125,8 @@ Rake::RDocTask.new(:docs) do |rd|
|
|
124
125
|
rd.options << "-t #{title}"
|
125
126
|
end
|
126
127
|
|
127
|
-
desc "Update
|
128
|
-
task :
|
128
|
+
desc "Update ChangeLog from the SVN log"
|
129
|
+
task :changelog do |t|
|
129
130
|
ARGV.shift
|
130
|
-
exec "svn2cl --break-before-msg -o
|
131
|
+
exec "svn2cl --break-before-msg -o ChangeLog #{ARGV.join(' ')}"
|
131
132
|
end
|
data/TODO
CHANGED
@@ -1,31 +1,32 @@
|
|
1
|
-
|
1
|
+
rename model_class to entity_class, and related terminology clarification.
|
2
|
+
tests
|
3
|
+
allow directories for a setup. Problem here is tab order.
|
4
|
+
metadata for virtual fields
|
5
|
+
exception when ditto-left/right to a field with incompatible type
|
2
6
|
generate models - DrySQL
|
7
|
+
allow moving of tabs
|
8
|
+
habtm relations
|
9
|
+
has_many :through
|
10
|
+
composed_of & aggregates
|
11
|
+
generate schema from definition? See rubyforge
|
3
12
|
Ctrl-PgDn to last row in this column. Also extend selection
|
4
13
|
sorting by header. See void QAbstractItemModel::sort ( int column, Qt::SortOrder order = Qt::AscendingOrder )
|
5
14
|
- layoutChanged
|
6
15
|
|
7
|
-
search with acts_as_searchable and hyperestraier
|
8
|
-
|
9
16
|
undo
|
10
17
|
- acts_as_trashable to undo deletes
|
11
18
|
- other commands. Possibly via ActiveRecord callbacks?
|
12
19
|
- Keep a history of changes, ie xy, new. xy, changed. x,y copied etc.
|
20
|
+
- Use Transaction::Simple?
|
21
|
+
- undo of field changes
|
13
22
|
|
14
23
|
Using F4 to open list, and then selecting from the combo and exiting using Return (or tab?) doesn't set the correct value
|
15
24
|
wrap description, and allow Access-style zooming
|
16
|
-
|
17
|
-
search in relational fields doesn't work because the search value is compared to the id of the related record
|
18
|
-
Use Webrick as a way to export CSV reports?
|
19
|
-
See Ruport and Documatic for reports
|
20
|
-
|
21
|
-
generate models - DrySQL
|
22
25
|
Ctrl-PgDn to last row in this column. Also extend selection
|
23
26
|
sorting by header. See void QAbstractItemModel::sort ( int column, Qt::SortOrder order = Qt::AscendingOrder )
|
24
27
|
- layoutChanged
|
25
28
|
|
26
29
|
search with acts_as_searchable and hyperestraier
|
27
|
-
acts_as_trashable to undo deletes
|
28
|
-
implement undo of field changes
|
29
30
|
|
30
31
|
moving of columns
|
31
32
|
/-style keyboard search by selected column, or everything if no column selected
|
@@ -41,7 +42,7 @@ filtering by various things. http://doc.trolltech.com/4.3/qsortfilterproxymodel.
|
|
41
42
|
highlighting by various things
|
42
43
|
cut and paste (in model format)
|
43
44
|
|
44
|
-
drop cached model objects from CacheTable when they're not in use
|
45
|
+
drop cached model objects from CacheTable when they're not in use. WeakRef?
|
45
46
|
|
46
47
|
allow scroll viewport to centre when at end of dataset
|
47
48
|
QAbstractItemView::ScrollHint
|
@@ -51,6 +52,12 @@ QAbstractItemView::ScrollHint
|
|
51
52
|
\value PositionAtBottom Scroll to position the item at the bottom of the viewport.
|
52
53
|
\value PositionAtCenter Scroll to position the item at the center of the viewport.
|
53
54
|
|
55
|
+
Reports
|
56
|
+
-------
|
57
|
+
Use Webrick as a way to export CSV reports?
|
58
|
+
See Ruport and Documatic for reports
|
59
|
+
Ruby Datavision Bridge for reports
|
60
|
+
|
54
61
|
OSX
|
55
62
|
---
|
56
63
|
Check that qtruby4 runs on OSX and so does Clevic. It does. Very slowly on Mac Mini with Motorola.
|
@@ -83,7 +90,7 @@ shortcut sets, depending on which OS you're used. use QKeyEvent::matches ( QKeyS
|
|
83
90
|
|
84
91
|
Doing data capture, sort by id, but unfilter reverts to date/id rather than entry order
|
85
92
|
optional warnings for back-dated entries. Highlighting
|
86
|
-
make
|
93
|
+
make bsearch easier to install
|
87
94
|
|
88
95
|
handle db errors
|
89
96
|
easier way to run models, search LOAD_PATH
|
@@ -97,8 +104,11 @@ store previous searches, by model & app
|
|
97
104
|
|
98
105
|
maybe
|
99
106
|
-----
|
107
|
+
acts_as_shellable looks nices
|
108
|
+
consolidate read-only-ness checks
|
100
109
|
Look at DataMapper. Not suitable - need to declare properties.
|
101
110
|
use rubigen for creating model definition files
|
111
|
+
ActiveMDB for migrating.
|
102
112
|
allow moving of rows
|
103
113
|
discontiguous copying of entities/csv
|
104
114
|
multi-row copying
|
@@ -106,6 +116,11 @@ pasting of csv, into rectangular regions
|
|
106
116
|
collect a set of data requests to the model, and do them in one SQL query. See EntryTableView#moveCursor
|
107
117
|
Use SQL cursors for find & find_next?
|
108
118
|
Use roo to parse spreadsheets?
|
119
|
+
Criteria for SQL statement building http://criteria.rubyforge.org/
|
120
|
+
SqlCache
|
121
|
+
SqlStatement
|
122
|
+
Csv2Sql
|
123
|
+
QueryBuilder
|
109
124
|
|
110
125
|
Accounts
|
111
126
|
--------
|
data/bin/clevic
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
#! /usr/bin/ruby
|
2
2
|
|
3
|
+
require 'pathname'
|
4
|
+
|
3
5
|
require 'clevic/browser.rb'
|
6
|
+
require 'clevic/db_options.rb'
|
4
7
|
require 'optparse'
|
8
|
+
require 'active_support'
|
5
9
|
|
6
|
-
# find and require variations on file_path
|
7
|
-
def require_if( file_path )
|
8
|
-
require file_path if File.exist?( file_path ) || File.exist?( file_path + '.rb' )
|
9
|
-
end
|
10
|
-
|
11
10
|
$options = {}
|
12
11
|
oparser = OptionParser.new
|
13
12
|
oparser.banner = <<BANNER
|
@@ -22,6 +21,7 @@ oparser.separator ''
|
|
22
21
|
oparser.on( '-H', '--host HOST', 'RDBMS host', String ) { |o| $options[:host] = o }
|
23
22
|
oparser.on( '-u', '--user USERNAME', String ) { |o| $options[:username] = o }
|
24
23
|
oparser.on( '-p', '--pass PASSWORD', String ) { |o| $options[:password] = o }
|
24
|
+
oparser.on( '-P', '--profile PROFILE', String ) { |o| $options[:profile] = o }
|
25
25
|
oparser.on( '-t', '--table TABLE', 'Table to display', String ) { |o| $options[:table] = o }
|
26
26
|
oparser.on( '-d', '--database DATABASE', 'Database name', String ) { |o| $options[:database] = o }
|
27
27
|
oparser.on( '-D', '--debug' ) { |o| $options[:debug] = true }
|
@@ -38,14 +38,67 @@ if $options[:debug]
|
|
38
38
|
pp $options
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
raise "no model definition file specified"
|
41
|
+
class Pathname
|
42
|
+
# require this pathname if it exists
|
43
|
+
def require_if_exists
|
44
|
+
require realpath.to_s if exist?
|
45
|
+
end
|
47
46
|
end
|
48
47
|
|
48
|
+
def load_rails_models( root, config, models )
|
49
|
+
# initialize Rails
|
50
|
+
load config + 'environment.rb'
|
51
|
+
#~ puts "RAILS_ROOT: #{RAILS_ROOT.inspect}"
|
52
|
+
require 'initializer.rb'
|
53
|
+
Rails::Initializer.run do |config|
|
54
|
+
config.frameworks -= [ :action_mailer, :action_pack, :active_resource ]
|
55
|
+
end
|
56
|
+
|
57
|
+
# load lib/ files
|
58
|
+
$: << ( root + 'lib' ).realpath.to_s
|
59
|
+
( root + 'lib' ).children.each do |filename|
|
60
|
+
puts "filename: #{filename.inspect}"
|
61
|
+
load filename if filename.file?
|
62
|
+
end
|
63
|
+
|
64
|
+
# TODO check for Dirty in 2.1.x
|
65
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Dirty)
|
66
|
+
|
67
|
+
# load models
|
68
|
+
models.children.each_with_index do |filename,i|
|
69
|
+
#~ %w{subscriber}.map{|x| models + "#{x}.rb"}.each_with_index do |filename,i|
|
70
|
+
begin
|
71
|
+
load filename if filename.file?
|
72
|
+
rescue Exception => e
|
73
|
+
puts "Error loading #{filename.basename.to_s}: #{e.message.inspect}"
|
74
|
+
puts e.backtrace
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def load_models( pathname )
|
80
|
+
if pathname.directory?
|
81
|
+
config = pathname + 'config'
|
82
|
+
app = pathname + 'app'
|
83
|
+
models = app + 'models'
|
84
|
+
# check if this is a Rails directory
|
85
|
+
if config.exist? && app.exist? && models.exist?
|
86
|
+
# this is probably a Rails project"
|
87
|
+
load_rails_models( pathname, config, models )
|
88
|
+
end
|
89
|
+
else
|
90
|
+
# assume we have a single file, and try some variations
|
91
|
+
( pathname + '.rb' ).require_if_exists
|
92
|
+
pathname.require_if_exists
|
93
|
+
( pathname + '_models' ).require_if_exists
|
94
|
+
( pathname + '_models.rb' ).require_if_exists
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# load model files
|
99
|
+
raise "no model definition file specified" if args.empty?
|
100
|
+
args.each { |arg| load_models( Pathname.new( arg ) ) }
|
101
|
+
|
49
102
|
app = Qt::Application.new( args )
|
50
103
|
|
51
104
|
# show UI
|
data/config/hoe.rb
CHANGED
@@ -8,7 +8,7 @@ RUBYFORGE_PROJECT = 'clevic' # The unix name for your project
|
|
8
8
|
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
9
|
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
10
|
EXTRA_DEPENDENCIES = [
|
11
|
-
['qtext', '>=0.
|
11
|
+
['qtext', '>=0.5.0'],
|
12
12
|
['activerecord', '=2.0.2'],
|
13
13
|
['fastercsv', '>=1.2.3']
|
14
14
|
# This isn't always installed from gems
|
data/lib/clevic/browser.rb
CHANGED
@@ -1,22 +1,13 @@
|
|
1
1
|
require 'clevic/search_dialog.rb'
|
2
2
|
require 'clevic/ui/browser_ui.rb'
|
3
|
+
require 'clevic/record.rb'
|
4
|
+
require 'clevic.rb'
|
3
5
|
|
4
6
|
module Clevic
|
5
7
|
|
6
8
|
=begin rdoc
|
7
|
-
The main application class.
|
8
|
-
|
9
|
-
a ModelBuilder.
|
10
|
-
|
11
|
-
Clevic::TableView.new( Entry, parent ).create_model.new( Entry, parent ).create_model
|
12
|
-
.
|
13
|
-
.
|
14
|
-
.
|
15
|
-
end
|
16
|
-
|
17
|
-
Model instances may also implement <tt>self.key_press_event( event, current_index, view )</tt>
|
18
|
-
and <tt>self.data_changed( top_left_index, bottom_right_index, view )</tt> methods so that
|
19
|
-
they can respond to editing events and do Neat Stuff.
|
9
|
+
The main application class. Display as many tabs as there are Clevic::Record or ActiveRecord::Base
|
10
|
+
subclasses.
|
20
11
|
=end
|
21
12
|
class Browser < Qt::Widget
|
22
13
|
slots *%w{dump() refresh_table() filter_by_current(bool) next_tab() previous_tab() current_changed(int)}
|
@@ -38,26 +29,48 @@ class Browser < Qt::Widget
|
|
38
29
|
@layout.main_widget.layout.add_widget @tables_tab
|
39
30
|
@tables_tab.tab_bar.focus_policy = Qt::NoFocus
|
40
31
|
|
41
|
-
#
|
42
|
-
@layout.
|
43
|
-
|
44
|
-
|
32
|
+
# hide the file menu, for now
|
33
|
+
@layout.menubar.remove_action( @layout.menu_file.menu_action )
|
34
|
+
|
35
|
+
# tab navigation
|
45
36
|
@layout.action_next.connect SIGNAL( 'triggered()' ), &method( :next_tab )
|
46
37
|
@layout.action_previous.connect SIGNAL( 'triggered()' ), &method( :previous_tab )
|
47
|
-
|
48
|
-
|
49
|
-
@layout.
|
38
|
+
|
39
|
+
# dump model for current tab
|
40
|
+
@layout.action_dump.visible = $options[:debug]
|
41
|
+
@layout.action_dump.connect SIGNAL( 'triggered()' ), &method( :dump )
|
50
42
|
|
51
43
|
tables_tab.connect SIGNAL( 'currentChanged(int)' ), &method( :current_changed )
|
52
44
|
|
53
|
-
# as an example
|
54
|
-
#~ tables_tab.connect SIGNAL( 'currentChanged(int)' ) { |index| puts "other current_changed: #{index}" }
|
55
45
|
load_models
|
46
|
+
update_menus
|
56
47
|
end
|
57
48
|
|
58
|
-
|
49
|
+
def update_menus
|
50
|
+
# update edit menu
|
51
|
+
@layout.menu_edit.clear
|
52
|
+
|
53
|
+
# do the model-specific menu items first
|
54
|
+
table_view.model_actions.each do |action|
|
55
|
+
@layout.menu_edit.add_action( action )
|
56
|
+
end
|
57
|
+
|
58
|
+
# now do the generic edit items
|
59
|
+
table_view.edit_actions.each do |action|
|
60
|
+
@layout.menu_edit.add_action( action )
|
61
|
+
end
|
62
|
+
|
63
|
+
# update search menu
|
64
|
+
@layout.menu_search.clear
|
65
|
+
table_view.search_actions.each do |action|
|
66
|
+
@layout.menu_search.add_action( action )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# activated by Ctrl-Shift-D for debugging
|
59
71
|
def dump
|
60
|
-
puts "table_view.model: #{table_view.model.inspect}"
|
72
|
+
puts "table_view.model: #{table_view.model.inspect}"
|
73
|
+
puts "table_view.model.model_class: #{table_view.model.model_class.inspect}"
|
61
74
|
end
|
62
75
|
|
63
76
|
# return the Clevic::TableView object in the currently displayed tab
|
@@ -69,57 +82,6 @@ class Browser < Qt::Widget
|
|
69
82
|
@tables_tab
|
70
83
|
end
|
71
84
|
|
72
|
-
# display a search dialog, and find the entered text
|
73
|
-
def find
|
74
|
-
@search_dialog ||= SearchDialog.new
|
75
|
-
result = @search_dialog.exec( table_view.current_index.gui_value )
|
76
|
-
|
77
|
-
override_cursor( Qt::BusyCursor ) do
|
78
|
-
case result
|
79
|
-
when Qt::Dialog::Accepted
|
80
|
-
search_for = @search_dialog.search_text
|
81
|
-
table_view.search( @search_dialog )
|
82
|
-
when Qt::Dialog::Rejected
|
83
|
-
puts "Don't search"
|
84
|
-
else
|
85
|
-
puts "unknown dialog code #{result}"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def find_next
|
91
|
-
if @search_dialog.nil?
|
92
|
-
@layout.statusbar.show_message( 'No previous find' )
|
93
|
-
else
|
94
|
-
override_cursor( Qt::BusyCursor ) do
|
95
|
-
save_from_start = @search_dialog.from_start?
|
96
|
-
@search_dialog.from_start = false
|
97
|
-
table_view.search( @search_dialog )
|
98
|
-
@search_dialog.from_start = save_from_start
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# force a complete reload of the current tab's data
|
104
|
-
def refresh_table
|
105
|
-
override_cursor( Qt::BusyCursor ) do
|
106
|
-
table_view.model.reload_data
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# toggle the filter, based on current selection.
|
111
|
-
def filter_by_current( bool_filter )
|
112
|
-
# TODO if there's no selection, use the current index instead
|
113
|
-
table_view.filter_by_indexes( table_view.selection_model.selected_indexes )
|
114
|
-
|
115
|
-
# set the checkbox in the menu item
|
116
|
-
@layout.action_filter.checked = table_view.filtered
|
117
|
-
|
118
|
-
# update the tab, so there's a visual indication of filtering
|
119
|
-
tab_title = table_view.filtered ? translate( '| ' + table_view.model_class.name.humanize ) : translate( table_view.model_class.name.humanize )
|
120
|
-
tables_tab.set_tab_text( tables_tab.current_index, tab_title )
|
121
|
-
end
|
122
|
-
|
123
85
|
# slot to handle Ctrl-Tab and move to next tab, or wrap around
|
124
86
|
def next_tab
|
125
87
|
tables_tab.current_index =
|
@@ -140,15 +102,11 @@ class Browser < Qt::Widget
|
|
140
102
|
end
|
141
103
|
end
|
142
104
|
|
143
|
-
def new_row
|
144
|
-
table_view.model.add_new_item
|
145
|
-
end
|
146
|
-
|
147
105
|
# slot to handle the currentChanged signal from tables_tab, and
|
148
106
|
# set focus on the grid
|
149
107
|
def current_changed( current_tab_index )
|
150
|
-
|
151
|
-
|
108
|
+
update_menus
|
109
|
+
tables_tab.current_widget.set_focus
|
152
110
|
end
|
153
111
|
|
154
112
|
# shortcut for the Qt translate call
|
@@ -156,37 +114,20 @@ class Browser < Qt::Widget
|
|
156
114
|
Qt::Application.translate("Browser", st, nil, Qt::Application::UnicodeUTF8)
|
157
115
|
end
|
158
116
|
|
159
|
-
# return the list of descendants of ActiveRecord::Base
|
117
|
+
# return the list of descendants of ActiveRecord::Base, or
|
118
|
+
# of Clevic::Record
|
160
119
|
def find_models
|
161
120
|
models = []
|
162
|
-
ObjectSpace.each_object( Class )
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
# DrySQL to automate the process.
|
169
|
-
def define_default_ui( model )
|
170
|
-
reflections = model.reflections.keys.map{|x| x.to_s}
|
171
|
-
ui_columns = model.columns.reject{|x| x.name == 'id' }.map do |x|
|
172
|
-
att = x.name.gsub( '_id', '' )
|
173
|
-
if reflections.include?( att )
|
174
|
-
att
|
175
|
-
else
|
176
|
-
x.name
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
Clevic::TableView.new( model, tables_tab ).create_model do
|
181
|
-
ui_columns.each do |column|
|
182
|
-
if model.reflections.has_key?( column.to_sym )
|
183
|
-
relational column.to_sym
|
184
|
-
else
|
185
|
-
plain column.to_sym
|
121
|
+
ObjectSpace.each_object( Class ) do |x|
|
122
|
+
if x.ancestors.include?( ActiveRecord::Base )
|
123
|
+
case
|
124
|
+
when x == ActiveRecord::Base; # don't include this
|
125
|
+
when x == Clevic::Record; # don't include this
|
126
|
+
else; models << x
|
186
127
|
end
|
187
128
|
end
|
188
|
-
records :order => 'id'
|
189
129
|
end
|
130
|
+
models.sort{|a,b| a.name <=> b.name}
|
190
131
|
end
|
191
132
|
|
192
133
|
# Create the tabs, each with a collection for a particular model class.
|
@@ -194,18 +135,47 @@ class Browser < Qt::Widget
|
|
194
135
|
# models parameter can be an array of Model objects, in order of display.
|
195
136
|
# if models is nil, find_models is called
|
196
137
|
def load_models
|
197
|
-
models = Clevic::Record.models
|
138
|
+
models = Clevic::Record.models
|
139
|
+
models = find_models if models.empty?
|
198
140
|
|
199
141
|
# Add all existing model objects as tabs, one each
|
200
|
-
models.each do |
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
142
|
+
models.each do |model_class|
|
143
|
+
begin
|
144
|
+
next unless model_class.table_exists?
|
145
|
+
|
146
|
+
# create the the table_view and the table_model for the model_class
|
147
|
+
tab =
|
148
|
+
if model_class.respond_to?( :ui )
|
149
|
+
puts "Entity#ui deprecated. Use build_table_model instead."
|
150
|
+
model_class.ui( tables_tab )
|
151
|
+
else
|
152
|
+
Clevic::TableView.new( model_class, tables_tab )
|
153
|
+
end
|
154
|
+
|
155
|
+
# show status messages
|
156
|
+
tab.connect( SIGNAL( 'status_text(QString)' ) ) { |msg| @layout.statusbar.show_message( msg, 10000 ) }
|
157
|
+
|
158
|
+
# add a new tab
|
159
|
+
tables_tab.add_tab( tab, translate( model_class.name.demodulize.tableize.humanize ) )
|
160
|
+
|
161
|
+
# add the table to the Table menu
|
162
|
+
action = Qt::Action.new( @layout.menu_model )
|
163
|
+
action.text = translate( model_class.name.demodulize.tableize.humanize )
|
164
|
+
action.connect SIGNAL( 'triggered()' ) do
|
165
|
+
tables_tab.current_widget = tab
|
166
|
+
end
|
167
|
+
@layout.menu_model.add_action( action )
|
168
|
+
|
169
|
+
# handle filter status changed, so we can provide a visual indication
|
170
|
+
tab.connect SIGNAL( 'filter_status(bool)' ) do |status|
|
171
|
+
# update the tab, so there's a visual indication of filtering
|
172
|
+
tab_title = ( tab.filtered ? '| ' : '' ) + translate( model_class.name.humanize )
|
173
|
+
tables_tab.set_tab_text( tables_tab.current_index, tab_title )
|
174
|
+
end
|
175
|
+
rescue Exception => e
|
176
|
+
puts e.backtrace if $options[:debug]
|
177
|
+
puts "Model #{model_class} will not be available: #{e.message}"
|
206
178
|
end
|
207
|
-
tab.connect( SIGNAL( 'status_text(QString)' ) ) { |msg| @layout.statusbar.show_message( msg, 20000 ) }
|
208
|
-
tables_tab.add_tab( tab, translate( model.name.humanize ) )
|
209
179
|
end
|
210
180
|
end
|
211
181
|
|