clevic 0.8.0 → 0.11.1
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 +9 -0
- data/Manifest.txt +13 -10
- data/README.txt +6 -9
- data/Rakefile +35 -24
- data/TODO +29 -17
- data/bin/clevic +84 -37
- data/config/hoe.rb +7 -3
- data/lib/clevic.rb +2 -4
- data/lib/clevic/browser.rb +37 -49
- data/lib/clevic/cache_table.rb +55 -165
- data/lib/clevic/db_options.rb +32 -21
- data/lib/clevic/default_view.rb +66 -0
- data/lib/clevic/delegates.rb +51 -67
- data/lib/clevic/dirty.rb +101 -0
- data/lib/clevic/extensions.rb +24 -38
- data/lib/clevic/field.rb +400 -99
- data/lib/clevic/item_delegate.rb +32 -33
- data/lib/clevic/model_builder.rb +315 -148
- data/lib/clevic/order_attribute.rb +53 -0
- data/lib/clevic/record.rb +57 -57
- data/lib/clevic/search_dialog.rb +71 -67
- data/lib/clevic/sql_dialects.rb +33 -0
- data/lib/clevic/table_model.rb +73 -120
- data/lib/clevic/table_searcher.rb +165 -0
- data/lib/clevic/table_view.rb +140 -100
- data/lib/clevic/ui/.gitignore +1 -0
- data/lib/clevic/ui/browser_ui.rb +55 -56
- data/lib/clevic/ui/search_dialog_ui.rb +50 -51
- data/lib/clevic/version.rb +2 -2
- data/lib/clevic/view.rb +89 -0
- data/models/accounts_models.rb +12 -9
- data/models/minimal_models.rb +4 -2
- data/models/times_models.rb +41 -25
- data/models/times_sqlite_models.rb +1 -145
- data/models/values_models.rb +15 -16
- data/test/test_cache_table.rb +138 -0
- data/test/test_helper.rb +131 -0
- data/test/test_model_index_extensions.rb +22 -0
- data/test/test_order_attribute.rb +62 -0
- data/test/test_sql_dialects.rb +77 -0
- data/test/test_table_searcher.rb +188 -0
- metadata +36 -20
- data/bin/import-times +0 -128
- data/config/jamis.rb +0 -589
- data/env.sh +0 -1
- data/lib/active_record/dirty.rb +0 -87
- data/lib/clevic/field_builder.rb +0 -42
- data/website/index.html +0 -170
- data/website/index.txt +0 -17
- data/website/screenshot.png +0 -0
- data/website/stylesheets/screen.css +0 -131
- data/website/template.html.erb +0 -41
data/env.sh
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
export RUBYLIB=$RUBYLIB:`pwd`/lib
|
data/lib/active_record/dirty.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
# Track unsaved changes.
|
3
|
-
module Dirty
|
4
|
-
def self.included(base)
|
5
|
-
base.attribute_method_suffix '_changed?', '_change', '_original'
|
6
|
-
base.alias_method_chain :read_attribute, :dirty
|
7
|
-
base.alias_method_chain :write_attribute, :dirty
|
8
|
-
base.alias_method_chain :save, :dirty
|
9
|
-
end
|
10
|
-
|
11
|
-
# Do any attributes have unsaved changes?
|
12
|
-
# person.changed? # => false
|
13
|
-
# person.name = 'bob'
|
14
|
-
# person.changed? # => true
|
15
|
-
def changed?
|
16
|
-
!changed_attributes.empty?
|
17
|
-
end
|
18
|
-
|
19
|
-
# List of attributes with unsaved changes.
|
20
|
-
# person.changed # => []
|
21
|
-
# person.name = 'bob'
|
22
|
-
# person.changed # => ['name']
|
23
|
-
def changed
|
24
|
-
changed_attributes.keys
|
25
|
-
end
|
26
|
-
|
27
|
-
# Map of changed attrs => [original value, new value]
|
28
|
-
# person.changes # => {}
|
29
|
-
# person.name = 'bob'
|
30
|
-
# person.changes # => { 'name' => ['bill', 'bob'] }
|
31
|
-
def changes
|
32
|
-
changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
# Clear changed attributes after they are saved.
|
37
|
-
def save_with_dirty(*args) #:nodoc:
|
38
|
-
save_without_dirty(*args)
|
39
|
-
ensure
|
40
|
-
changed_attributes.clear
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
# Map of change attr => original value.
|
45
|
-
def changed_attributes
|
46
|
-
@changed_attributes ||= {}
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
# Wrap read_attribute to freeze its result.
|
51
|
-
def read_attribute_with_dirty(attr)
|
52
|
-
read_attribute_without_dirty(attr).freeze
|
53
|
-
end
|
54
|
-
|
55
|
-
# Wrap write_attribute to remember original attribute value.
|
56
|
-
def write_attribute_with_dirty(attr, value)
|
57
|
-
attr = attr.to_s
|
58
|
-
|
59
|
-
# The attribute already has an unsaved change.
|
60
|
-
unless changed_attributes.include?(attr)
|
61
|
-
old = read_attribute(attr)
|
62
|
-
|
63
|
-
# Remember the original value if it's different.
|
64
|
-
changed_attributes[attr] = old unless old == value
|
65
|
-
end
|
66
|
-
|
67
|
-
# Carry on.
|
68
|
-
write_attribute_without_dirty(attr, value)
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# Handle *_changed? for method_missing.
|
73
|
-
def attribute_changed?(attr)
|
74
|
-
changed_attributes.include?(attr)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Handle *_change for method_missing.
|
78
|
-
def attribute_change(attr)
|
79
|
-
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Handle *_original for method_missing.
|
83
|
-
def attribute_original(attr)
|
84
|
-
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
data/lib/clevic/field_builder.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Clevic
|
2
|
-
|
3
|
-
class BlankSlate
|
4
|
-
keep_methods = %w( __send__ __id__ send class inspect instance_eval instance_variables )
|
5
|
-
instance_methods.each do |m|
|
6
|
-
undef_method(m) unless keep_methods.include?(m)
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
class FieldBuilder < BlankSlate
|
11
|
-
def initialize( hash = {} )
|
12
|
-
@hash = hash
|
13
|
-
end
|
14
|
-
|
15
|
-
# modified from Jim Freeze's article
|
16
|
-
def self.dsl_accessor(*symbols)
|
17
|
-
symbols.each do |sym|
|
18
|
-
line, st = __LINE__, <<EOF
|
19
|
-
def #{sym}(*val)
|
20
|
-
if val.empty?
|
21
|
-
@hash[#{sym.to_sym.inspect}]
|
22
|
-
else
|
23
|
-
@hash[#{sym.to_sym.inspect}] = val.size == 1 ? val[0] : val
|
24
|
-
end
|
25
|
-
end
|
26
|
-
EOF
|
27
|
-
class_eval st, __FILE__, line + 1
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# originally from Jim Freeze's article
|
32
|
-
def method_missing(sym, *args)
|
33
|
-
self.class.dsl_accessor sym
|
34
|
-
send(sym, *args)
|
35
|
-
end
|
36
|
-
|
37
|
-
def to_hash
|
38
|
-
@hash
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
end
|
data/website/index.html
DELETED
@@ -1,170 +0,0 @@
|
|
1
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
-
<head>
|
5
|
-
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" />
|
6
|
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
7
|
-
<title>
|
8
|
-
Clevic
|
9
|
-
</title>
|
10
|
-
<style>
|
11
|
-
|
12
|
-
</style>
|
13
|
-
<script type="text/javascript">
|
14
|
-
window.onload = function() {
|
15
|
-
settings = {
|
16
|
-
tl: { radius: 10 },
|
17
|
-
tr: { radius: 10 },
|
18
|
-
bl: { radius: 10 },
|
19
|
-
br: { radius: 10 },
|
20
|
-
antiAlias: true,
|
21
|
-
autoPad: true,
|
22
|
-
validTags: ["div"]
|
23
|
-
}
|
24
|
-
}
|
25
|
-
</script>
|
26
|
-
</head>
|
27
|
-
<body>
|
28
|
-
|
29
|
-
<div id="main">
|
30
|
-
<h1>Clevic</h1>
|
31
|
-
<p><a href="rdoc">RDoc</a> | <a href="http://rubyforge.org/projects/clevic/">Rubyforge Project</a></p>
|
32
|
-
|
33
|
-
|
34
|
-
<p>Code for minimal UI definition</p>
|
35
|
-
|
36
|
-
|
37
|
-
<pre><code>
|
38
|
-
require 'clevic.rb'
|
39
|
-
|
40
|
-
# see sql/accounts.sql for schema
|
41
|
-
|
42
|
-
# db connection
|
43
|
-
Clevic::DbOptions.connect do
|
44
|
-
database 'accounts_test'
|
45
|
-
adapter :postgresql
|
46
|
-
username 'accounts'
|
47
|
-
end
|
48
|
-
|
49
|
-
# minimal definition to get combo boxes to show up
|
50
|
-
class Entry < Clevic::Record
|
51
|
-
belongs_to :debit, :class_name => 'Account', :foreign_key => 'debit_id'
|
52
|
-
belongs_to :credit, :class_name => 'Account', :foreign_key => 'credit_id'
|
53
|
-
end
|
54
|
-
|
55
|
-
# minimal definition to get sensible values in combo boxes
|
56
|
-
class Account < Clevic::Record
|
57
|
-
def to_s; name; end
|
58
|
-
end
|
59
|
-
|
60
|
-
</code></pre>
|
61
|
-
|
62
|
-
<p>Screenshot of a more fully defined UI with the foreign-key dropdown in place. Tabs contain
|
63
|
-
the two tables, model definition file is below the screenshot. The Entry
|
64
|
-
model has some code to do update the credit and debit fields when
|
65
|
-
the new item description is found in the table.</p>
|
66
|
-
|
67
|
-
|
68
|
-
<p><img src="screenshot.png" title="Screenshot" alt="Screenshot" /></p>
|
69
|
-
</div>
|
70
|
-
|
71
|
-
<pre><code>
|
72
|
-
require 'clevic.rb'
|
73
|
-
|
74
|
-
# db connection
|
75
|
-
Clevic::DbOptions.connect( $options ) do
|
76
|
-
# use a different db for testing, so real data doesn't get broken.
|
77
|
-
if options[:database].nil? || options[:database].empty?
|
78
|
-
database( debug? ? :accounts_test : :accounts )
|
79
|
-
else
|
80
|
-
database options[:database]
|
81
|
-
end
|
82
|
-
adapter :postgresql
|
83
|
-
username 'accounts'
|
84
|
-
end
|
85
|
-
|
86
|
-
class Entry < Clevic::Record
|
87
|
-
belongs_to :debit, :class_name => 'Account', :foreign_key => 'debit_id'
|
88
|
-
belongs_to :credit, :class_name => 'Account', :foreign_key => 'credit_id'
|
89
|
-
|
90
|
-
define_ui do
|
91
|
-
plain :date, :sample => '88-WWW-99'
|
92
|
-
distinct :description, :conditions => "now() - date <= '1 year'", :sample => 'm' * 26
|
93
|
-
relational :debit, :display => 'name', :conditions => 'active = true', :order => 'lower(name)', :sample => 'Leilani Member Loan'
|
94
|
-
relational :credit, :display => 'name', :conditions => 'active = true', :order => 'lower(name)', :sample => 'Leilani Member Loan'
|
95
|
-
plain :amount, :sample => 999999.99
|
96
|
-
distinct :category
|
97
|
-
plain :cheque_number
|
98
|
-
plain :active, :sample => 'WW'
|
99
|
-
plain :vat, :label => 'VAT', :sample => 'WW', :tooltip => 'Does this include VAT?'
|
100
|
-
|
101
|
-
records :order => 'date, id'
|
102
|
-
end
|
103
|
-
|
104
|
-
# called when data is changed in the UI
|
105
|
-
def self.data_changed( top_left, bottom_right, view )
|
106
|
-
if top_left == bottom_right
|
107
|
-
update_credit_debit( top_left, view )
|
108
|
-
else
|
109
|
-
puts "top_left: #{top_left.inspect}"
|
110
|
-
puts "bottom_right: #{bottom_right.inspect}"
|
111
|
-
puts "can't do data_changed for a range"
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
# check that the current field is :descriptions, then
|
116
|
-
# copy the values for the credit and debit fields
|
117
|
-
# from the previous similar entry
|
118
|
-
def self.update_credit_debit( current_index, view )
|
119
|
-
return if !current_index.valid?
|
120
|
-
current_field = current_index.attribute
|
121
|
-
if current_field == :description
|
122
|
-
# most recent entry, ordered in reverse
|
123
|
-
similar = self.find(
|
124
|
-
:first,
|
125
|
-
:conditions => ["#{current_field} = ?", current_index.attribute_value],
|
126
|
-
:order => 'date desc'
|
127
|
-
)
|
128
|
-
if similar != nil
|
129
|
-
# set the values
|
130
|
-
current_index.entity.debit = similar.debit
|
131
|
-
current_index.entity.credit = similar.credit
|
132
|
-
current_index.entity.category = similar.category
|
133
|
-
|
134
|
-
# emit signal to update view from top_left to bottom_right
|
135
|
-
model = current_index.model
|
136
|
-
top_left_index = model.create_index( current_index.row, 0 )
|
137
|
-
bottom_right_index = model.create_index( current_index.row, view.builder.fields.size )
|
138
|
-
view.dataChanged( top_left_index, bottom_right_index )
|
139
|
-
|
140
|
-
# move edit cursor to amount field
|
141
|
-
view.selection_model.clear
|
142
|
-
view.override_next_index( model.create_index( current_index.row, view.builder.index( :amount ) ) )
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
class Account < Clevic::Record
|
149
|
-
has_many :debits, :class_name => 'Entry', :foreign_key => 'debit_id'
|
150
|
-
has_many :credits, :class_name => 'Entry', :foreign_key => 'credit_id'
|
151
|
-
|
152
|
-
# define how fields are displayed
|
153
|
-
define_ui do
|
154
|
-
plain :name
|
155
|
-
restricted :vat, :label => 'VAT', :set => %w{ yes no all }
|
156
|
-
plain :account_type
|
157
|
-
plain :pastel_number, :alignment => Qt::AlignRight, :label => 'Pastel'
|
158
|
-
plain :fringe, :format => "%.1f"
|
159
|
-
plain :active
|
160
|
-
|
161
|
-
records :order => 'name,account_type'
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
</code></pre>
|
166
|
-
|
167
|
-
<!-- insert site tracking codes here, like Google Urchin -->
|
168
|
-
|
169
|
-
</body>
|
170
|
-
</html>
|
data/website/index.txt
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
h1. Clevic
|
2
|
-
|
3
|
-
"RDoc":rdoc | "Rubyforge Project":http://rubyforge.org/projects/clevic/
|
4
|
-
|
5
|
-
Code for minimal UI definition
|
6
|
-
|
7
|
-
<pre><code>
|
8
|
-
<%= File.read 'models/minimal_models.rb' %>
|
9
|
-
</code></pre>
|
10
|
-
|
11
|
-
|
12
|
-
Screenshot of a more fully defined UI with the foreign-key dropdown in place. Tabs contain
|
13
|
-
the two tables, model definition file is below the screenshot. The Entry
|
14
|
-
model has some code to do update the credit and debit fields when
|
15
|
-
the new item description is found in the table.
|
16
|
-
|
17
|
-
!screenshot.png(Screenshot)!
|
data/website/screenshot.png
DELETED
Binary file
|
@@ -1,131 +0,0 @@
|
|
1
|
-
body {
|
2
|
-
font-family: "Georgia", sans-serif;
|
3
|
-
font-size: 16px;
|
4
|
-
line-height: 1.6em;
|
5
|
-
padding: 1.6em 0 0 0;
|
6
|
-
color: #333;
|
7
|
-
}
|
8
|
-
h1, h2, h3, h4, h5, h6 {
|
9
|
-
color: #444;
|
10
|
-
}
|
11
|
-
h1 {
|
12
|
-
font-family: sans-serif;
|
13
|
-
font-weight: normal;
|
14
|
-
font-size: 4em;
|
15
|
-
line-height: 0.8em;
|
16
|
-
letter-spacing: -0.1ex;
|
17
|
-
margin: 5px;
|
18
|
-
}
|
19
|
-
li {
|
20
|
-
padding: 0;
|
21
|
-
margin: 0;
|
22
|
-
list-style-type: square;
|
23
|
-
}
|
24
|
-
blockquote {
|
25
|
-
font-size: 90%;
|
26
|
-
font-style: italic;
|
27
|
-
border-left: 1px solid #111;
|
28
|
-
padding-left: 1em;
|
29
|
-
}
|
30
|
-
.caps {
|
31
|
-
font-size: 80%;
|
32
|
-
}
|
33
|
-
|
34
|
-
#main {
|
35
|
-
width: 45em;
|
36
|
-
padding: 0;
|
37
|
-
margin: 0 auto;
|
38
|
-
}
|
39
|
-
.coda {
|
40
|
-
text-align: right;
|
41
|
-
color: #77f;
|
42
|
-
font-size: smaller;
|
43
|
-
}
|
44
|
-
|
45
|
-
table {
|
46
|
-
font-size: 90%;
|
47
|
-
line-height: 1.4em;
|
48
|
-
color: #ff8;
|
49
|
-
background-color: #111;
|
50
|
-
padding: 2px 10px 2px 10px;
|
51
|
-
border-style: dashed;
|
52
|
-
}
|
53
|
-
|
54
|
-
th {
|
55
|
-
color: #fff;
|
56
|
-
}
|
57
|
-
|
58
|
-
td {
|
59
|
-
padding: 2px 10px 2px 10px;
|
60
|
-
}
|
61
|
-
|
62
|
-
.success {
|
63
|
-
color: #0CC52B;
|
64
|
-
}
|
65
|
-
|
66
|
-
.failed {
|
67
|
-
color: #E90A1B;
|
68
|
-
}
|
69
|
-
|
70
|
-
.unknown {
|
71
|
-
color: #995000;
|
72
|
-
}
|
73
|
-
pre, code {
|
74
|
-
font-family: monospace;
|
75
|
-
font-size: 90%;
|
76
|
-
line-height: 1.4em;
|
77
|
-
color: #ff8;
|
78
|
-
background-color: #111;
|
79
|
-
padding: 2px 10px 2px 10px;
|
80
|
-
}
|
81
|
-
.comment { color: #aaa; font-style: italic; }
|
82
|
-
.keyword { color: #eff; font-weight: bold; }
|
83
|
-
.punct { color: #eee; font-weight: bold; }
|
84
|
-
.symbol { color: #0bb; }
|
85
|
-
.string { color: #6b4; }
|
86
|
-
.ident { color: #ff8; }
|
87
|
-
.constant { color: #66f; }
|
88
|
-
.regex { color: #ec6; }
|
89
|
-
.number { color: #F99; }
|
90
|
-
.expr { color: #227; }
|
91
|
-
|
92
|
-
#version {
|
93
|
-
float: right;
|
94
|
-
text-align: right;
|
95
|
-
font-family: sans-serif;
|
96
|
-
font-weight: normal;
|
97
|
-
background-color: #B3ABFF;
|
98
|
-
color: #141331;
|
99
|
-
padding: 15px 20px 10px 20px;
|
100
|
-
margin: 0 auto;
|
101
|
-
margin-top: 15px;
|
102
|
-
border: 3px solid #141331;
|
103
|
-
}
|
104
|
-
|
105
|
-
#version .numbers {
|
106
|
-
display: block;
|
107
|
-
font-size: 4em;
|
108
|
-
line-height: 0.8em;
|
109
|
-
letter-spacing: -0.1ex;
|
110
|
-
margin-bottom: 15px;
|
111
|
-
}
|
112
|
-
|
113
|
-
#version p {
|
114
|
-
text-decoration: none;
|
115
|
-
color: #141331;
|
116
|
-
background-color: #B3ABFF;
|
117
|
-
margin: 0;
|
118
|
-
padding: 0;
|
119
|
-
}
|
120
|
-
|
121
|
-
#version a {
|
122
|
-
text-decoration: none;
|
123
|
-
color: #141331;
|
124
|
-
background-color: #B3ABFF;
|
125
|
-
}
|
126
|
-
|
127
|
-
.clickable {
|
128
|
-
cursor: pointer;
|
129
|
-
cursor: hand;
|
130
|
-
}
|
131
|
-
|