clevic 0.7.0 → 0.8.0
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.
- 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
|
|