Dex 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/db.db +0 -0
- data/lib/Dex.rb +85 -41
- data/lib/Dex/version.rb +1 -1
- data/spec/Dex.rb +92 -68
- data/spec/libs/main.rb +17 -11
- metadata +3 -2
data/db.db
ADDED
File without changes
|
data/lib/Dex.rb
CHANGED
@@ -3,62 +3,101 @@ require 'sequel'
|
|
3
3
|
|
4
4
|
class Dex
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
module DEFAULT
|
7
|
+
DB_NAME = "/tmp/dex_exceptions.db"
|
8
|
+
TABLE_NAME = :dex_exceptions
|
9
|
+
RACK_DIR = File.join( File.dirname( __FILE__ ) , "/Dex/Rack" )
|
8
10
|
end
|
9
11
|
|
10
|
-
def self.
|
11
|
-
"
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.default_table
|
15
|
-
:dex_exceptions
|
12
|
+
def self.default k
|
13
|
+
eval "#{self}::DEFAULT::#{k.to_s.upcase}"
|
16
14
|
end
|
17
15
|
|
18
16
|
module DSL
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
attr_reader :table
|
19
|
+
|
20
|
+
def default *args
|
21
|
+
Dex.default(*args)
|
23
22
|
end
|
24
23
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
def db *args
|
25
|
+
return @db if args.empty? && instance_variable_defined?(:@db)
|
26
|
+
|
27
|
+
case args.size
|
28
|
+
|
29
|
+
when 0
|
30
|
+
name = default :db_name
|
31
|
+
table = default :table_name
|
32
|
+
|
33
|
+
when 1
|
34
|
+
name = args[0]
|
35
|
+
table = default :table_name
|
36
|
+
|
37
|
+
when 2
|
38
|
+
name = args[0]
|
39
|
+
table = args[1]
|
40
|
+
|
41
|
+
else
|
42
|
+
args.shift
|
43
|
+
args.shift
|
44
|
+
raise ArgumentError, "Unknown arguments: #{args.inspect}"
|
45
|
+
|
46
|
+
end # === case
|
47
|
+
|
48
|
+
@db = Sequel.sqlite(name)
|
49
|
+
@table = @db[table.to_sym]
|
50
|
+
|
51
|
+
@db.create_table?(table_name) {
|
52
|
+
|
53
|
+
primary_key :id
|
54
|
+
String :message
|
55
|
+
String :exception
|
56
|
+
Text :backtrace
|
57
|
+
Integer :status
|
58
|
+
DateTime :created_at
|
59
|
+
|
60
|
+
}
|
61
|
+
|
62
|
+
@db
|
63
|
+
end # === def db
|
64
|
+
|
65
|
+
def db_name
|
66
|
+
db.opts[:database] || default(:db_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def table_name
|
70
|
+
return nil unless table
|
71
|
+
table.opts[:from].first
|
29
72
|
end
|
30
73
|
|
31
|
-
def
|
32
|
-
|
33
|
-
|
34
|
-
db_file name
|
35
|
-
db = Sequel.sqlite db_file
|
36
|
-
db.create_table?(Dex.default_table) {
|
37
|
-
|
38
|
-
primary_key :id
|
39
|
-
String :message
|
40
|
-
String :exception
|
41
|
-
Text :backtrace
|
42
|
-
Integer :status
|
43
|
-
DateTime :created_at
|
44
|
-
|
45
|
-
}
|
46
|
-
db
|
47
|
-
end
|
48
|
-
@table = nil
|
49
|
-
end
|
74
|
+
def table_exists?
|
75
|
+
db.table_exists?(table_name)
|
76
|
+
end
|
50
77
|
|
51
|
-
|
78
|
+
def fields
|
79
|
+
db.schema(table_name).map(&:first)
|
52
80
|
end
|
53
81
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@table ||= table(:dex_exceptions)
|
82
|
+
def keep_only n = 250
|
83
|
+
c = table.count
|
84
|
+
return false unless c > 250
|
85
|
+
table.filter( :id=> Dex.table.select(:id).limit( c-n ) ).delete
|
59
86
|
end
|
60
87
|
|
61
|
-
def insert e
|
88
|
+
def insert e, other=Hash[]
|
89
|
+
unless other.keys.empty?
|
90
|
+
keys=other.keys.map(&:to_sym)
|
91
|
+
new_keys = keys - fields
|
92
|
+
unless new_keys.empty?
|
93
|
+
db.alter_table table_name do
|
94
|
+
new_keys.each { |k|
|
95
|
+
add_column k, :string
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
62
101
|
table.insert \
|
63
102
|
:message => e.message, \
|
64
103
|
:exception => e.exception.class.name, \
|
@@ -67,6 +106,11 @@ class Dex
|
|
67
106
|
:created_at => Time.now.utc
|
68
107
|
end
|
69
108
|
|
109
|
+
def remove_field name
|
110
|
+
db.alter_table table_name do
|
111
|
+
drop_column name
|
112
|
+
end
|
113
|
+
end
|
70
114
|
def recent n = 10
|
71
115
|
ds = table.reverse_order(:created_at, :id).limit(n)
|
72
116
|
if n < 2
|
data/lib/Dex/version.rb
CHANGED
data/spec/Dex.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
|
2
2
|
describe "Dex :db" do
|
3
|
-
|
4
|
-
|
3
|
+
|
4
|
+
behaves_like 'Test DB'
|
5
|
+
|
6
|
+
it "converts String table name to Symbol. (Sequel table name compatibility.)" do
|
7
|
+
t = new_dex
|
8
|
+
t.db "/tmp/db.test.1.db", "my_table"
|
9
|
+
t.table_name.should == "my_table".to_sym
|
10
|
+
end
|
11
|
+
|
12
|
+
it "sets table name to specified value" do
|
5
13
|
t = new_dex
|
6
|
-
t.db "/tmp/db.test.1.db"
|
7
|
-
t.
|
8
|
-
t.db "/tmp/db.test.2.db"
|
9
|
-
t.instance_eval { @table }.should.be == nil
|
14
|
+
t.db "/tmp/db.test.1.db", :my_new_table
|
15
|
+
t.table_name.should == :my_new_table
|
10
16
|
end
|
11
17
|
|
12
18
|
it "allows file names with underscores: my_log.db" do
|
@@ -36,102 +42,120 @@ describe "Dex :db" do
|
|
36
42
|
end # === Dex :db
|
37
43
|
|
38
44
|
describe "Dex :recent" do
|
39
|
-
|
45
|
+
|
46
|
+
behaves_like 'Test DB'
|
47
|
+
|
40
48
|
it "returns first result if n = 1" do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Dex.recent( 1 )[:message].should == "One"
|
45
|
-
}
|
49
|
+
e = except "One"
|
50
|
+
Dex.insert e
|
51
|
+
Dex.recent( 1 )[:message].should == "One"
|
46
52
|
end
|
47
53
|
|
48
54
|
it "returns a dataset if n > 1" do
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
r.to_a.size.should == 2
|
56
|
-
}
|
55
|
+
e1 = except "One"
|
56
|
+
e2 = except "Two"
|
57
|
+
Dex.insert e1
|
58
|
+
Dex.insert e2
|
59
|
+
r = Dex.recent
|
60
|
+
r.to_a.size.should == 2
|
57
61
|
end
|
58
|
-
|
62
|
+
|
59
63
|
it "returns results in reverse order" do
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
Dex.recent.map { |d| d[:message] }.should == %w{ 2 1 0 }
|
65
|
-
}
|
64
|
+
3.times do |i|
|
65
|
+
Dex.insert( except i.to_s )
|
66
|
+
end
|
67
|
+
Dex.recent.map { |d| d[:message] }.should == %w{ 2 1 0 }
|
66
68
|
end
|
67
69
|
|
68
70
|
end # === Dex :recent
|
69
71
|
|
70
72
|
|
71
73
|
describe "Dex :insert" do
|
72
|
-
|
74
|
+
|
75
|
+
behaves_like 'Test DB'
|
76
|
+
|
73
77
|
it "saves message of exception" do
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
Dex.recent( 1 )[:message].should == e.message
|
78
|
-
}
|
78
|
+
e = except "My Name"
|
79
|
+
Dex.insert e
|
80
|
+
Dex.recent( 1 )[:message].should == e.message
|
79
81
|
end
|
80
|
-
|
82
|
+
|
81
83
|
it "returns id of new record" do
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
Dex.filter(:id=>id).first[:message].should == "My record"
|
86
|
-
}
|
84
|
+
e = except "My record"
|
85
|
+
id = Dex.insert(e)
|
86
|
+
Dex.filter(:id=>id).first[:message].should == "My record"
|
87
87
|
end
|
88
88
|
|
89
89
|
it "sets :status to 0 by default" do
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
id = Dex.insert(except "Another record")
|
91
|
+
Dex.filter(:id=>id).first[:status].should == 0
|
92
|
+
end
|
93
|
+
|
94
|
+
it "adds new fields to table" do
|
95
|
+
dex = new_dex "new_fields"
|
96
|
+
dex.db.transaction(:rollback=>:always) {
|
97
|
+
dex.insert( except("New fields"), :fd1=>"field 1", :fd2=>"field 2" )
|
98
|
+
fields = dex.db.schema(dex.table_name).map(&:first)
|
99
|
+
fields.should.include :fd1
|
100
|
+
fields.should.include :fd2
|
93
101
|
}
|
94
102
|
end
|
103
|
+
|
95
104
|
end # === Dex :insert
|
96
105
|
|
97
106
|
describe "Dex :keep_only" do
|
98
|
-
|
107
|
+
|
108
|
+
behaves_like 'Test DB'
|
109
|
+
|
99
110
|
it "deletes oldest records leaving most recent 250" do
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
Dex.recent(1)[:message].should == '299'
|
105
|
-
}
|
111
|
+
300.times { |i| Dex.insert except(i.to_s) }
|
112
|
+
Dex.keep_only
|
113
|
+
Dex.count.should == 250
|
114
|
+
Dex.recent(1)[:message].should == '299'
|
106
115
|
end
|
107
|
-
|
116
|
+
|
108
117
|
it "accepts a limit argument" do
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
Dex.table.count.should == 12
|
113
|
-
}
|
118
|
+
300.times { |i| Dex.insert except(i.to_s) }
|
119
|
+
Dex.keep_only 12
|
120
|
+
Dex.table.count.should == 12
|
114
121
|
end
|
115
122
|
|
116
123
|
end # === Dex :keep_only
|
117
124
|
|
118
|
-
describe "Dex missing_method" do
|
119
|
-
|
125
|
+
describe "Dex :missing_method" do
|
126
|
+
|
127
|
+
behaves_like 'Test DB'
|
128
|
+
|
120
129
|
it "sends message to :table if it responds to message" do
|
121
|
-
|
122
|
-
|
123
|
-
Dex.count.should == Dex.table.count
|
124
|
-
}
|
130
|
+
(rand 10).times { |i| Dex.insert except(i.to_s) }
|
131
|
+
Dex.count.should == Dex.table.count
|
125
132
|
end
|
126
133
|
|
127
134
|
it "raises super :missing_method if :table does not respond to message" do
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
}.message.should.match %r!undefined method `xyz' for Dex:Class!
|
133
|
-
}
|
135
|
+
(rand 10).times { |i| Dex.insert except(i.to_s) }
|
136
|
+
should.raise(NoMethodError) {
|
137
|
+
Dex.xyz
|
138
|
+
}.message.should.match %r!undefined method `xyz' for Dex:Class!
|
134
139
|
end
|
135
|
-
|
140
|
+
|
136
141
|
end # === Dex missing_method
|
137
142
|
|
143
|
+
describe "Dex :remove_field" do
|
144
|
+
|
145
|
+
before {
|
146
|
+
@dex = new_dex(':memory:')
|
147
|
+
@dex.table.delete
|
148
|
+
}
|
149
|
+
|
150
|
+
it "removes specified field" do
|
151
|
+
orig = @dex.fields
|
152
|
+
name = :my_field
|
153
|
+
@dex.insert except("remve field"), name => '---'
|
154
|
+
@dex.fields.should.include name
|
155
|
+
@dex.remove_field name
|
156
|
+
|
157
|
+
@dex.fields.should.not.include name
|
158
|
+
end
|
159
|
+
|
160
|
+
end # === Dex :remove_field
|
161
|
+
|
data/spec/libs/main.rb
CHANGED
@@ -18,21 +18,18 @@ Bacon.summary_on_exit
|
|
18
18
|
require 'Bacon_Colored'
|
19
19
|
require 'pry'
|
20
20
|
require 'Exit_0'
|
21
|
-
|
22
21
|
require 'Dex'
|
23
22
|
|
24
|
-
|
25
|
-
Dex.db "/tmp/dex.test.db"
|
23
|
+
Dex.db ":memory:"
|
26
24
|
|
27
|
-
def
|
28
|
-
Dex.db.transaction(:rollback=>:always) {
|
29
|
-
yield
|
30
|
-
}
|
31
|
-
end
|
32
|
-
|
33
|
-
def new_dex
|
25
|
+
def new_dex db = nil
|
34
26
|
@t ||= Class.new { include Dex::DSL }
|
35
|
-
@t.new
|
27
|
+
dex = @t.new
|
28
|
+
if db
|
29
|
+
dex.db(db[':'] ? db : File.join("/tmp", db) )
|
30
|
+
end
|
31
|
+
|
32
|
+
dex
|
36
33
|
end
|
37
34
|
|
38
35
|
def except name
|
@@ -46,6 +43,15 @@ def except name
|
|
46
43
|
end
|
47
44
|
|
48
45
|
|
46
|
+
shared "Test DB" do
|
47
|
+
|
48
|
+
before {
|
49
|
+
Dex.table.delete
|
50
|
+
}
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
49
55
|
# ======== Include the tests.
|
50
56
|
if ARGV.size > 1 && ARGV[1, ARGV.size - 1].detect { |a| File.exists?(a) }
|
51
57
|
# Do nothing. Bacon grabs the file.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Dex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-05-
|
12
|
+
date: 2012-05-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bacon
|
@@ -135,6 +135,7 @@ files:
|
|
135
135
|
- Gemfile
|
136
136
|
- README.md
|
137
137
|
- Rakefile
|
138
|
+
- db.db
|
138
139
|
- lib/Dex.rb
|
139
140
|
- lib/Dex/version.rb
|
140
141
|
- spec/Dex.rb
|