clevic 0.8.0 → 0.11.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/test/test_helper.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'shoulda'
|
3
|
+
|
4
|
+
require File.dirname(__FILE__) + '/../lib/clevic'
|
5
|
+
|
6
|
+
require 'activerecord'
|
7
|
+
require 'sqlite3'
|
8
|
+
require 'faker'
|
9
|
+
require 'generator'
|
10
|
+
|
11
|
+
|
12
|
+
class Flight < ActiveRecord::Base
|
13
|
+
has_many :passengers
|
14
|
+
end
|
15
|
+
|
16
|
+
class Passenger < ActiveRecord::Base
|
17
|
+
belongs_to :flight
|
18
|
+
end
|
19
|
+
|
20
|
+
class CreateFlights < ActiveRecord::Migration
|
21
|
+
def self.up
|
22
|
+
create_table :flights do |t|
|
23
|
+
t.string :number
|
24
|
+
t.string :airline
|
25
|
+
t.string :destination
|
26
|
+
end
|
27
|
+
Flight.reset_column_information
|
28
|
+
Flight.create :number => 'EK211'
|
29
|
+
Flight.create :number => 'EK088'
|
30
|
+
Flight.create :number => 'EK761'
|
31
|
+
Flight.create :number => 'BA264'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.down
|
35
|
+
Flight.delete_all
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class CreatePassengers < ActiveRecord::Migration
|
40
|
+
def self.up
|
41
|
+
create_table :passengers do |t|
|
42
|
+
t.string :name
|
43
|
+
t.string :nationality
|
44
|
+
t.integer :flight_id
|
45
|
+
t.integer :row
|
46
|
+
t.string :seat
|
47
|
+
end
|
48
|
+
Passenger.reset_column_information
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.down
|
52
|
+
Passenger.delete_all
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Convenience class to create a test DB
|
57
|
+
class OneBase
|
58
|
+
attr_reader :db_name, :adapter
|
59
|
+
|
60
|
+
def initialize
|
61
|
+
@db_name = 'test_cache_table.sqlite3'
|
62
|
+
|
63
|
+
if File.exists? @db_name
|
64
|
+
p 'remove old db'
|
65
|
+
File.unlink @db_name
|
66
|
+
end
|
67
|
+
|
68
|
+
@adapter = :sqlite3
|
69
|
+
@db = SQLite3::Database.new( @db_name )
|
70
|
+
@db_options = Clevic::DbOptions.connect do |dbo|
|
71
|
+
dbo.database @db_name
|
72
|
+
dbo.adapter @adapter
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def feenesh
|
77
|
+
File.unlink @db_name
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Allow running of startup and shutdown things before
|
82
|
+
# an entire suite, instead of just one per test
|
83
|
+
class SuiteWrapper < Test::Unit::TestSuite
|
84
|
+
attr_accessor :tests
|
85
|
+
|
86
|
+
def initialize( name, test_case )
|
87
|
+
super( name )
|
88
|
+
@test_case = test_case
|
89
|
+
end
|
90
|
+
|
91
|
+
def startup
|
92
|
+
@onebase = OneBase.new
|
93
|
+
ActiveRecord::Migration.verbose = false
|
94
|
+
CreateFlights.up
|
95
|
+
CreatePassengers.up
|
96
|
+
end
|
97
|
+
|
98
|
+
def shutdown
|
99
|
+
CreatePassengers.down
|
100
|
+
CreateFlights.down
|
101
|
+
@onebase.feenesh
|
102
|
+
end
|
103
|
+
|
104
|
+
def run( *args )
|
105
|
+
startup
|
106
|
+
@test_case.startup if @test_case.respond_to? :startup
|
107
|
+
retval = super
|
108
|
+
@test_case.shutdown if @test_case.respond_to? :shutdown
|
109
|
+
shutdown
|
110
|
+
retval
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module Test
|
115
|
+
module Unit
|
116
|
+
class TestCase
|
117
|
+
unless respond_to? :old_suite
|
118
|
+
class << self
|
119
|
+
alias_method :old_suite, :suite
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.suite
|
123
|
+
os = old_suite
|
124
|
+
sw = SuiteWrapper.new( os.name, self )
|
125
|
+
sw.tests = os.tests
|
126
|
+
sw
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class TestModelIndex < Test::Unit::TestCase
|
4
|
+
def self.startup
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.shutdown
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
end
|
15
|
+
|
16
|
+
should_eventually 'test something'
|
17
|
+
|
18
|
+
should 'be true' do
|
19
|
+
assert true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'clevic/order_attribute.rb'
|
3
|
+
|
4
|
+
class Dummy < ActiveRecord::Base
|
5
|
+
end
|
6
|
+
|
7
|
+
# need to set up a test DB, and test data for this
|
8
|
+
class TestOrderAttribute < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_reverse
|
16
|
+
oa = OrderAttribute.new Dummy, 'id'
|
17
|
+
assert_equal :asc, oa.reverse( :desc )
|
18
|
+
assert_equal :desc, oa.reverse( :asc )
|
19
|
+
assert_raise( RuntimeError ) { oa.reverse( :something_wrong ) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Test that initialisation was OK
|
23
|
+
def test_equal
|
24
|
+
oa1 = OrderAttribute.new Dummy, 'id'
|
25
|
+
oa2 = OrderAttribute.new Dummy, 'id'
|
26
|
+
assert_equal oa1, oa2
|
27
|
+
assert_equal oa1.to_sql, 'dummies.id asc'
|
28
|
+
assert_equal oa1.to_reverse_sql, 'dummies.id desc'
|
29
|
+
assert_equal oa1.attribute.to_sym, oa1.to_sym
|
30
|
+
|
31
|
+
assert_equal oa2.to_sql, 'dummies.id asc'
|
32
|
+
assert_equal oa2.to_reverse_sql, 'dummies.id desc'
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_parse_default
|
36
|
+
oa_asc = OrderAttribute.new Dummy, "name"
|
37
|
+
assert_equal 'name', oa_asc.attribute
|
38
|
+
assert_equal :asc, oa_asc.direction
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_parse_desc
|
42
|
+
oa_desc = OrderAttribute.new Dummy, "name desc"
|
43
|
+
assert_equal 'name', oa_desc.attribute
|
44
|
+
assert_equal 'name', oa_desc.to_s
|
45
|
+
assert_equal :desc, oa_desc.direction
|
46
|
+
assert_equal oa_desc.to_sql, 'dummies.name desc'
|
47
|
+
assert_equal 'dummies.name asc', oa_desc.to_reverse_sql
|
48
|
+
|
49
|
+
oa_desc = OrderAttribute.new Dummy, "dummies.name desc"
|
50
|
+
assert_equal 'name', oa_desc.attribute
|
51
|
+
assert_equal :desc, oa_desc.direction
|
52
|
+
assert_equal oa_desc.to_sql, 'dummies.name desc'
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_parse_table
|
56
|
+
oa_with_table = OrderAttribute.new Dummy, 'dummies.name asc'
|
57
|
+
assert_equal 'name', oa_with_table.attribute
|
58
|
+
assert_equal :asc, oa_with_table.direction
|
59
|
+
assert_equal oa_with_table.to_sql, 'dummies.name asc'
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'clevic/sql_dialects.rb'
|
3
|
+
|
4
|
+
# TODO should probably connect to real DB drivers to do this
|
5
|
+
|
6
|
+
class MockPostgreSQL
|
7
|
+
include Clevic::SqlDialects
|
8
|
+
def adapter_name
|
9
|
+
'PostgreSQL'
|
10
|
+
end
|
11
|
+
|
12
|
+
def connection
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class MockOther
|
19
|
+
include Clevic::SqlDialects
|
20
|
+
def adapter_name
|
21
|
+
'Something else entirely'
|
22
|
+
end
|
23
|
+
|
24
|
+
def connection
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def quoted_true; "'t'"; end
|
29
|
+
def quoted_false; "'f'"; end
|
30
|
+
end
|
31
|
+
|
32
|
+
class TestSqlDialects < Test::Unit::TestCase
|
33
|
+
def self.startup
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.shutdown
|
37
|
+
end
|
38
|
+
|
39
|
+
def setup
|
40
|
+
end
|
41
|
+
|
42
|
+
context MockPostgreSQL.name do
|
43
|
+
setup do
|
44
|
+
@dialect = MockPostgreSQL.new
|
45
|
+
end
|
46
|
+
|
47
|
+
should 'return ilike' do
|
48
|
+
assert_equal 'ilike', @dialect.like_operator
|
49
|
+
end
|
50
|
+
|
51
|
+
should "return true" do
|
52
|
+
assert_equal "true", @dialect.sql_boolean( true )
|
53
|
+
end
|
54
|
+
|
55
|
+
should "return false" do
|
56
|
+
assert_equal "false", @dialect.sql_boolean( false )
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context MockOther.name do
|
61
|
+
setup do
|
62
|
+
@dialect = MockOther.new
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'return like' do
|
66
|
+
assert_equal 'like', @dialect.like_operator
|
67
|
+
end
|
68
|
+
|
69
|
+
should "return 't'" do
|
70
|
+
assert_equal "'t'", @dialect.sql_boolean( true )
|
71
|
+
end
|
72
|
+
|
73
|
+
should "return 'f'" do
|
74
|
+
assert_equal "'f'", @dialect.sql_boolean( false )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
require 'clevic/table_searcher.rb'
|
3
|
+
|
4
|
+
class CreateFakePassengers < ActiveRecord::Migration
|
5
|
+
MAX_PASSENGERS = 100
|
6
|
+
NATIONALITIES = %w{Canada USA Britain UAE}
|
7
|
+
|
8
|
+
def self.up
|
9
|
+
1.upto( MAX_PASSENGERS ) do |i|
|
10
|
+
Passenger.create :name => Faker::Name.name, :flight => Flight.find(:all)[i%4], :nationality => NATIONALITIES[i%4], :row => i, :seat => %w{A B C D}[i % 4]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.down
|
15
|
+
Passenger.delete_all
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class MockSearchCriteria
|
20
|
+
|
21
|
+
def initialize( &block )
|
22
|
+
@direction = :forwards
|
23
|
+
@from_start = false
|
24
|
+
@whole_words = false
|
25
|
+
self.instance_eval( &block ) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :direction, :search_text
|
29
|
+
attr_writer :whole_words, :from_start
|
30
|
+
def whole_words?; @whole_words; end
|
31
|
+
def from_start?; @from_start; end
|
32
|
+
end
|
33
|
+
|
34
|
+
class TestTableSearcher < Test::Unit::TestCase
|
35
|
+
def self.startup
|
36
|
+
CreateFakePassengers.up
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.shutdown
|
40
|
+
CreateFakePassengers.down
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup
|
44
|
+
@simple_search_criteria = MockSearchCriteria.new
|
45
|
+
@id_order_attribute = OrderAttribute.new( Passenger, 'id' )
|
46
|
+
|
47
|
+
@name_field = Clevic::Field.new( :name, Passenger, {} )
|
48
|
+
@nationality_field = Clevic::Field.new( :nationality, Passenger, {} )
|
49
|
+
@all_passengers = Passenger.find( :all, :conditions => [ 'flight_id = ?', Flight.find(:first).id ], :order => :id )
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'on initialisation' do
|
53
|
+
should "have a matching field attribute on construction" do
|
54
|
+
ts = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @name_field )
|
55
|
+
assert_equal @name_field.attribute, ts.field.attribute
|
56
|
+
end
|
57
|
+
|
58
|
+
should "throw an exception when called with no order attributes" do
|
59
|
+
assert_raise( RuntimeError ) do
|
60
|
+
Clevic::TableSearcher.new( Passenger, nil, @simple_search_criteria, @name_field )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
should "throw an exception when called with an empty collection of order attributes" do
|
65
|
+
assert_raise( RuntimeError ) do
|
66
|
+
Clevic::TableSearcher.new( Passenger, [], @simple_search_criteria, @name_field )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
should "throw an exception when called with no field" do
|
71
|
+
assert_raise( RuntimeError ) do
|
72
|
+
Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, nil )
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
should 'throw an exception for an unknown direction' do
|
77
|
+
@simple_search_criteria.direction = :other
|
78
|
+
assert_raise( RuntimeError ) do
|
79
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "searching" do
|
85
|
+
setup do
|
86
|
+
@simple_search_criteria.search_text = CreateFakePassengers::NATIONALITIES[0]
|
87
|
+
@passenger_generator = Generator.new( @all_passengers )
|
88
|
+
end
|
89
|
+
|
90
|
+
should "have #{CreateFakePassengers::MAX_PASSENGERS} passengers" do
|
91
|
+
assert_equal CreateFakePassengers::MAX_PASSENGERS, Passenger.count
|
92
|
+
end
|
93
|
+
|
94
|
+
should_eventually "do more granular testing"
|
95
|
+
|
96
|
+
should "find the first record" do
|
97
|
+
@simple_search_criteria.from_start = true
|
98
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
99
|
+
assert_equal @all_passengers.first, table_searcher.search
|
100
|
+
end
|
101
|
+
|
102
|
+
should "backwards-find the last record" do
|
103
|
+
@simple_search_criteria.from_start = true
|
104
|
+
@simple_search_criteria.direction = :backwards
|
105
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
106
|
+
assert_equal @all_passengers.last, table_searcher.search
|
107
|
+
end
|
108
|
+
|
109
|
+
should "backwards-find the next-to-last record" do
|
110
|
+
# find the last record
|
111
|
+
@simple_search_criteria.from_start = true
|
112
|
+
@simple_search_criteria.direction = :backwards
|
113
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
114
|
+
last = table_searcher.search
|
115
|
+
|
116
|
+
# now find the previous record
|
117
|
+
@simple_search_criteria.from_start = false
|
118
|
+
assert_equal @all_passengers[-2], table_searcher.search( last )
|
119
|
+
end
|
120
|
+
|
121
|
+
should "find next records" do
|
122
|
+
@simple_search_criteria.from_start = false
|
123
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
124
|
+
|
125
|
+
# fetch records one by one, starting from the one after the first one, and compare them
|
126
|
+
while next_entity = table_searcher.search( @passenger_generator.next )
|
127
|
+
passenger = @passenger_generator.next
|
128
|
+
|
129
|
+
assert_equal next_entity, passenger
|
130
|
+
assert_not_equal @all_passengers.first, passenger
|
131
|
+
|
132
|
+
last_entity = next_entity
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'search for related field value' do
|
138
|
+
should 'raise an exception for no display value' do
|
139
|
+
@simple_search_criteria.from_start = true
|
140
|
+
@simple_search_criteria.search_text = Flight.find(:first).number
|
141
|
+
flight_field = Clevic::Field.new( :flight, Passenger, {} )
|
142
|
+
assert_nil flight_field.path
|
143
|
+
assert_raise RuntimeError do
|
144
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, flight_field )
|
145
|
+
table_searcher.search
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
should 'find a record' do
|
150
|
+
@simple_search_criteria.from_start = true
|
151
|
+
@simple_search_criteria.search_text = Flight.find(:first).number
|
152
|
+
flight_field = Clevic::Field.new( :flight, Passenger, { :display => 'number' } )
|
153
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, flight_field )
|
154
|
+
assert_equal @all_passengers.first, table_searcher.search
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'whole words' do
|
159
|
+
setup do
|
160
|
+
@simple_search_criteria.from_start = true
|
161
|
+
@simple_search_criteria.search_text = CreateFakePassengers::NATIONALITIES[0][0..-3]
|
162
|
+
@should_find = Passenger.find :all, :conditions => "nationality like '%#{@simple_search_criteria.search_text}%'", :order => :id
|
163
|
+
end
|
164
|
+
|
165
|
+
should 'find a full value with a partial search string' do
|
166
|
+
@simple_search_criteria.whole_words = false
|
167
|
+
@simple_search_criteria.from_start = true
|
168
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
169
|
+
g = Generator.new @should_find
|
170
|
+
last_entity = nil
|
171
|
+
while next_entity = table_searcher.search( last_entity )
|
172
|
+
assert_equal next_entity, g.next
|
173
|
+
last_entity = next_entity
|
174
|
+
@simple_search_criteria.from_start = false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
should 'not find any values with a partial search string and whole_words enabled' do
|
179
|
+
@simple_search_criteria.whole_words = true
|
180
|
+
@simple_search_criteria.from_start = true
|
181
|
+
table_searcher = Clevic::TableSearcher.new( Passenger, [@id_order_attribute], @simple_search_criteria, @nationality_field )
|
182
|
+
assert_nil table_searcher.search
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
should_eventually 'work for Array'
|
187
|
+
|
188
|
+
end
|