oculus 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +34 -0
- data/Rakefile +18 -0
- data/TODO.md +12 -0
- data/bin/oculus +32 -0
- data/features/query.feature +26 -0
- data/features/step_definitions/query_steps.rb +35 -0
- data/features/support/env.rb +22 -0
- data/lib/oculus/connection/mysql2.rb +22 -0
- data/lib/oculus/connection.rb +7 -0
- data/lib/oculus/presenters/query_presenter.rb +40 -0
- data/lib/oculus/presenters.rb +1 -0
- data/lib/oculus/query.rb +61 -0
- data/lib/oculus/server/public/css/bootstrap.min.css +689 -0
- data/lib/oculus/server/public/css/codemirror.css +112 -0
- data/lib/oculus/server/public/css/reset.css +47 -0
- data/lib/oculus/server/public/css/style.css +107 -0
- data/lib/oculus/server/public/img/glyphicons-halflings-white.png +0 -0
- data/lib/oculus/server/public/img/glyphicons-halflings.png +0 -0
- data/lib/oculus/server/public/js/application.js +160 -0
- data/lib/oculus/server/public/js/bootstrap.min.js +6 -0
- data/lib/oculus/server/public/js/codemirror.min.js +1 -0
- data/lib/oculus/server/public/js/jquery.min.js +4 -0
- data/lib/oculus/server/public/js/spin.min.js +2 -0
- data/lib/oculus/server/views/history.erb +35 -0
- data/lib/oculus/server/views/index.erb +97 -0
- data/lib/oculus/server/views/layout.erb +37 -0
- data/lib/oculus/server/views/show.erb +59 -0
- data/lib/oculus/server.rb +88 -0
- data/lib/oculus/storage/file_store.rb +129 -0
- data/lib/oculus/storage.rb +7 -0
- data/lib/oculus/version.rb +3 -0
- data/lib/oculus.rb +29 -0
- data/oculus.gemspec +26 -0
- data/spec/connection_spec.rb +61 -0
- data/spec/file_store_spec.rb +111 -0
- data/spec/query_presenter_spec.rb +54 -0
- data/spec/query_spec.rb +107 -0
- metadata +173 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module Oculus
|
5
|
+
module Storage
|
6
|
+
class FileStore
|
7
|
+
def initialize(root)
|
8
|
+
@root = root
|
9
|
+
end
|
10
|
+
|
11
|
+
def all_queries
|
12
|
+
Dir["#{root}/*.query"].map do |path|
|
13
|
+
File.parse(path)
|
14
|
+
end.sort { |a,b| b.id <=> a.id }
|
15
|
+
end
|
16
|
+
|
17
|
+
def save_query(query)
|
18
|
+
query.id = next_id if query.id.nil?
|
19
|
+
|
20
|
+
File.open(filename_for_id(query.id), 'w') do |file|
|
21
|
+
file.write_prelude(query.attributes)
|
22
|
+
file.write_results(query.results) if query.results
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_query(id)
|
27
|
+
path = filename_for_id(id)
|
28
|
+
|
29
|
+
if File.exist?(path)
|
30
|
+
File.parse(path)
|
31
|
+
else
|
32
|
+
raise QueryNotFound, id
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_query(id)
|
37
|
+
path = filename_for_id(id)
|
38
|
+
|
39
|
+
if File.exist?(path)
|
40
|
+
File.unlink(path)
|
41
|
+
else
|
42
|
+
raise QueryNotFound, id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
class File < ::File
|
49
|
+
def self.parse(path)
|
50
|
+
file = File.open(path)
|
51
|
+
attributes = file.attributes
|
52
|
+
attributes[:results] = file.results
|
53
|
+
Oculus::Query.new(attributes).tap do |query|
|
54
|
+
query.id = File.basename(path).split('.').first.to_i
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def write_prelude(attributes)
|
59
|
+
write(YAML.dump(attributes))
|
60
|
+
puts("---")
|
61
|
+
end
|
62
|
+
|
63
|
+
def write_results(results)
|
64
|
+
csv_data = CSV.generate do |csv|
|
65
|
+
csv << results.first
|
66
|
+
results[1..-1].each do |result|
|
67
|
+
csv << result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
write csv_data
|
72
|
+
end
|
73
|
+
|
74
|
+
def attributes
|
75
|
+
rewind
|
76
|
+
|
77
|
+
raw = gets
|
78
|
+
|
79
|
+
until (line = gets) == "---\n"
|
80
|
+
raw += line
|
81
|
+
end
|
82
|
+
|
83
|
+
YAML.load(raw)
|
84
|
+
end
|
85
|
+
|
86
|
+
def results
|
87
|
+
rewind
|
88
|
+
|
89
|
+
section = 0
|
90
|
+
section += 1 if gets.rstrip == "---" until section == 2 || eof?
|
91
|
+
|
92
|
+
CSV.new(read).to_a
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def filename_for_id(id)
|
97
|
+
raise ArgumentError unless id.is_a?(Integer) || id =~ /^[0-9]+/
|
98
|
+
File.join(root, "#{id}.query")
|
99
|
+
end
|
100
|
+
|
101
|
+
def next_id
|
102
|
+
reset_primary_key unless File.exist?(primary_key_path)
|
103
|
+
id_file = File.open(primary_key_path, 'r+')
|
104
|
+
id_file.flock(File::LOCK_EX)
|
105
|
+
|
106
|
+
id = id_file.read.to_i
|
107
|
+
|
108
|
+
id_file.rewind
|
109
|
+
id_file.write(id + 1)
|
110
|
+
id_file.flock(File::LOCK_UN)
|
111
|
+
id_file.close
|
112
|
+
|
113
|
+
id
|
114
|
+
end
|
115
|
+
|
116
|
+
def reset_primary_key
|
117
|
+
File.open(primary_key_path, 'w') do |file|
|
118
|
+
file.puts '0'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def primary_key_path
|
123
|
+
File.join(root, 'NEXT_ID')
|
124
|
+
end
|
125
|
+
|
126
|
+
attr_reader :root
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/oculus.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "oculus/version"
|
2
|
+
require "oculus/storage"
|
3
|
+
require "oculus/connection"
|
4
|
+
require "oculus/query"
|
5
|
+
|
6
|
+
module Oculus
|
7
|
+
extend self
|
8
|
+
|
9
|
+
DEFAULT_CONNECTION_OPTIONS = { :host => 'localhost', :username => 'root' }
|
10
|
+
|
11
|
+
attr_writer :cache_path
|
12
|
+
|
13
|
+
def cache_path
|
14
|
+
@cache_path ||= 'tmp/data'
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_writer :data_store
|
18
|
+
|
19
|
+
def data_store
|
20
|
+
@data_store ||= Oculus::Storage::FileStore.new(Oculus.cache_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_writer :connection_options
|
24
|
+
|
25
|
+
def connection_options
|
26
|
+
@connection_options ||= DEFAULT_CONNECTION_OPTIONS
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/oculus.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/oculus/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Paul Rosania"]
|
6
|
+
gem.email = ["paul.rosania@gmail.com"]
|
7
|
+
gem.description = %q{Oculus is a web-based logging SQL client. It keeps a history of your queries and the results they returned, so your research is always at hand, easy to share and easy to repeat or reproduce in the future.}
|
8
|
+
gem.summary = %q{Oculus is a web-based logging SQL client.}
|
9
|
+
gem.homepage = "https://github.com/paulrosania/oculus"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "oculus"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Oculus::VERSION
|
17
|
+
|
18
|
+
gem.add_runtime_dependency "sinatra", [">= 1.3.0"]
|
19
|
+
gem.add_runtime_dependency "mysql2", [">= 0.3.11"]
|
20
|
+
gem.add_runtime_dependency "vegas", [">= 0.1.4"]
|
21
|
+
|
22
|
+
gem.add_development_dependency "rake"
|
23
|
+
gem.add_development_dependency "cucumber", [">= 1"]
|
24
|
+
gem.add_development_dependency "rspec", [">= 2"]
|
25
|
+
gem.add_development_dependency "capybara", [">= 1"]
|
26
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'oculus'
|
2
|
+
|
3
|
+
describe Oculus::Connection do
|
4
|
+
before(:all) do
|
5
|
+
client = Mysql2::Client.new(:host => "localhost", :username => "root")
|
6
|
+
client.query "CREATE DATABASE IF NOT EXISTS test"
|
7
|
+
client.query "USE test"
|
8
|
+
client.query %[
|
9
|
+
CREATE TABLE IF NOT EXISTS oculus_users (
|
10
|
+
id MEDIUMINT NOT NULL AUTO_INCREMENT,
|
11
|
+
name VARCHAR(255),
|
12
|
+
PRIMARY KEY (id)
|
13
|
+
);
|
14
|
+
]
|
15
|
+
|
16
|
+
client.query 'TRUNCATE oculus_users'
|
17
|
+
|
18
|
+
client.query %[
|
19
|
+
INSERT INTO oculus_users (name) VALUES ('Paul'), ('Amy'), ('Peter')
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { Oculus::Connection::Mysql2.new(:database => 'test') }
|
24
|
+
|
25
|
+
it "fetches a result set" do
|
26
|
+
subject.execute("SELECT * FROM oculus_users").should == [['id', 'name'], [1, 'Paul'], [2, 'Amy'], [3, 'Peter']]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns nil for queries that don't return result sets" do
|
30
|
+
query_connection = Mysql2::Client.new(:host => "localhost", :database => "test")
|
31
|
+
thread_id = query_connection.thread_id
|
32
|
+
Thread.new {
|
33
|
+
query_connection.execute("SELECT * FROM oculus_users WHERE SLEEP(2)")
|
34
|
+
}
|
35
|
+
|
36
|
+
sleep 0.1
|
37
|
+
subject.execute("KILL QUERY #{thread_id}").should be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises a Connection::Error on syntax errors" do
|
41
|
+
lambda {
|
42
|
+
subject.execute("FOO BAZ QUUX")
|
43
|
+
}.should raise_error(Oculus::Connection::Error)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raises a Connection::Error when the query is interrupted" do
|
47
|
+
thread_id = subject.thread_id
|
48
|
+
Thread.new {
|
49
|
+
sleep 0.1
|
50
|
+
Mysql2::Client.new(:host => "localhost", :username => "root").query("KILL QUERY #{thread_id}")
|
51
|
+
}
|
52
|
+
|
53
|
+
lambda {
|
54
|
+
subject.execute("SELECT * FROM oculus_users WHERE SLEEP(2)")
|
55
|
+
}.should raise_error(Oculus::Connection::Error)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "provides the connection's thread_id" do
|
59
|
+
subject.thread_id.should be_an Integer
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'oculus'
|
2
|
+
|
3
|
+
describe Oculus::Storage::FileStore do
|
4
|
+
subject { Oculus::Storage::FileStore.new('tmp/test_cache') }
|
5
|
+
|
6
|
+
let(:query) do
|
7
|
+
Oculus::Query.new(:name => "All users",
|
8
|
+
:query => "SELECT * FROM oculus_users",
|
9
|
+
:author => "Paul",
|
10
|
+
:thread_id => 42,
|
11
|
+
:results => [['id', 'users'], ['1', 'Paul'], ['2', 'Amy']])
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:other_query) do
|
15
|
+
Oculus::Query.new(:name => "Admin users",
|
16
|
+
:query => "SELECT * FROM oculus_users WHERE is_admin = 1",
|
17
|
+
:author => "Paul",
|
18
|
+
:thread_id => 42,
|
19
|
+
:results => [['id', 'users'], ['2', 'Amy']])
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:broken_query) do
|
23
|
+
Oculus::Query.new(:name => "Admin users",
|
24
|
+
:query => "FOO BAZ QUUX",
|
25
|
+
:author => "Paul",
|
26
|
+
:thread_id => 42,
|
27
|
+
:error => "You have an error in your SQL syntax")
|
28
|
+
end
|
29
|
+
|
30
|
+
before do
|
31
|
+
FileUtils.mkdir_p('tmp/test_cache')
|
32
|
+
end
|
33
|
+
|
34
|
+
after do
|
35
|
+
FileUtils.rm_r('tmp/test_cache')
|
36
|
+
end
|
37
|
+
|
38
|
+
it "round-trips a query with no results to disk" do
|
39
|
+
query = Oculus::Query.new(:name => "Unfinished query", :author => "Me")
|
40
|
+
subject.save_query(query)
|
41
|
+
subject.load_query(query.id).results.should == []
|
42
|
+
subject.load_query(query.id).query.should == query.query
|
43
|
+
subject.load_query(query.id).date.should == query.date
|
44
|
+
subject.load_query(query.id).author.should == query.author
|
45
|
+
subject.load_query(query.id).id.should == query.id
|
46
|
+
subject.load_query(query.id).thread_id.should == query.thread_id
|
47
|
+
end
|
48
|
+
|
49
|
+
it "round-trips a query with an error to disk" do
|
50
|
+
subject.save_query(broken_query)
|
51
|
+
subject.load_query(broken_query.id).results.should == []
|
52
|
+
subject.load_query(broken_query.id).error.should == broken_query.error
|
53
|
+
subject.load_query(broken_query.id).query.should == broken_query.query
|
54
|
+
subject.load_query(broken_query.id).date.should == broken_query.date
|
55
|
+
subject.load_query(broken_query.id).author.should == broken_query.author
|
56
|
+
subject.load_query(broken_query.id).id.should == broken_query.id
|
57
|
+
subject.load_query(broken_query.id).thread_id.should == broken_query.thread_id
|
58
|
+
end
|
59
|
+
|
60
|
+
it "round-trips a query to disk" do
|
61
|
+
subject.save_query(query)
|
62
|
+
subject.load_query(query.id).results.should == query.results
|
63
|
+
subject.load_query(query.id).query.should == query.query
|
64
|
+
subject.load_query(query.id).date.should == query.date
|
65
|
+
subject.load_query(query.id).author.should == query.author
|
66
|
+
subject.load_query(query.id).id.should == query.id
|
67
|
+
subject.load_query(query.id).thread_id.should == query.thread_id
|
68
|
+
end
|
69
|
+
|
70
|
+
it "doesn't overwrite an existing query id when saving" do
|
71
|
+
subject.save_query(query)
|
72
|
+
original_id = query.id
|
73
|
+
subject.save_query(query)
|
74
|
+
query.id.should == original_id
|
75
|
+
end
|
76
|
+
|
77
|
+
it "raises QueryNotFound for missing queries" do
|
78
|
+
lambda {
|
79
|
+
subject.load_query(39827493)
|
80
|
+
}.should raise_error(Oculus::Storage::QueryNotFound)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "fetches all queries in reverse chronological order" do
|
84
|
+
subject.save_query(query)
|
85
|
+
subject.save_query(other_query)
|
86
|
+
|
87
|
+
subject.all_queries.map(&:results).should == [other_query.results, query.results]
|
88
|
+
end
|
89
|
+
|
90
|
+
it "deletes queries" do
|
91
|
+
subject.save_query(query)
|
92
|
+
subject.load_query(query.id).name.should == query.name
|
93
|
+
subject.delete_query(query.id)
|
94
|
+
|
95
|
+
lambda {
|
96
|
+
subject.load_query(query.id)
|
97
|
+
}.should raise_error(Oculus::Storage::QueryNotFound)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises QueryNotFound when deleting a nonexistent query" do
|
101
|
+
lambda {
|
102
|
+
subject.delete_query(10983645)
|
103
|
+
}.should raise_error(Oculus::Storage::QueryNotFound)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "sanitizes query IDs" do
|
107
|
+
lambda {
|
108
|
+
subject.delete_query('..')
|
109
|
+
}.should raise_error(ArgumentError)
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative '../lib/oculus/query'
|
2
|
+
require_relative '../lib/oculus/presenters/query_presenter'
|
3
|
+
|
4
|
+
describe Oculus::Presenters::QueryPresenter do
|
5
|
+
let(:query) { Oculus::Query.new }
|
6
|
+
let(:presenter) { Oculus::Presenters::QueryPresenter.new(query) }
|
7
|
+
|
8
|
+
it "should delegate to the underlying query" do
|
9
|
+
query.name = 'foo'
|
10
|
+
presenter.description.should == 'foo'
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a formatted date" do
|
14
|
+
query.date = Time.mktime(2010, 1, 1, 12, 34)
|
15
|
+
presenter.formatted_date.should == '2010-01-01 12:34'
|
16
|
+
end
|
17
|
+
|
18
|
+
it "reports successful queries" do
|
19
|
+
query.stub(:complete?).and_return(true)
|
20
|
+
presenter.status.should == 'done'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "reports failed queries" do
|
24
|
+
query.stub(:complete?).and_return(true)
|
25
|
+
query.stub(:error).and_return("you fail")
|
26
|
+
presenter.status.should == 'error'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "reports loading queries" do
|
30
|
+
query.stub(:complete?).and_return(false)
|
31
|
+
presenter.status.should == 'loading'
|
32
|
+
end
|
33
|
+
|
34
|
+
it "uses name for a description when there is one" do
|
35
|
+
query.name = "foo"
|
36
|
+
presenter.description.should == "foo"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "uses SQL for a description when there isn't a name" do
|
40
|
+
query.name = nil
|
41
|
+
query.query = "SELECT * FROM foo"
|
42
|
+
presenter.description.should == "SELECT * FROM foo"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "reports that the query has been named" do
|
46
|
+
query.name = "Select all the things"
|
47
|
+
presenter.should be_named
|
48
|
+
end
|
49
|
+
|
50
|
+
it "reports that the query has not been named" do
|
51
|
+
query.name = nil
|
52
|
+
presenter.should_not be_named
|
53
|
+
end
|
54
|
+
end
|
data/spec/query_spec.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'oculus'
|
2
|
+
|
3
|
+
describe Oculus::Query do
|
4
|
+
before do
|
5
|
+
Oculus.data_store = stub
|
6
|
+
end
|
7
|
+
|
8
|
+
it "runs the query against the supplied connection" do
|
9
|
+
connection = stub
|
10
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
11
|
+
connection.should_receive(:execute).with('SELECT * FROM users')
|
12
|
+
query.execute(connection)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "stores the results of running the query" do
|
16
|
+
connection = stub(:execute => [['id', 'name'], [1, 'Paul']])
|
17
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
18
|
+
query.execute(connection)
|
19
|
+
query.results.should == [['id', 'name'], [1, 'Paul']]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "stores errors when queries fail" do
|
23
|
+
connection = stub
|
24
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
25
|
+
connection.stub(:execute).and_raise(Oculus::Connection::Error.new('You have an error in your SQL syntax'))
|
26
|
+
query.execute(connection)
|
27
|
+
query.error.should == 'You have an error in your SQL syntax'
|
28
|
+
end
|
29
|
+
|
30
|
+
it "stores the query itself" do
|
31
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
32
|
+
query.query.should == 'SELECT * FROM users'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "stores the querying connection's thread ID" do
|
36
|
+
query = Oculus::Query.new(:thread_id => 42)
|
37
|
+
query.thread_id.should == 42
|
38
|
+
end
|
39
|
+
|
40
|
+
it "has a date" do
|
41
|
+
query = Oculus::Query.new
|
42
|
+
query.date.should be nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "updates date on save" do
|
46
|
+
Oculus.data_store.stub(:save_query)
|
47
|
+
Time.stub(:now).and_return(now = stub)
|
48
|
+
query = Oculus::Query.create(:results => [['id', 'name'], [1, 'Paul']])
|
49
|
+
query.date.should == now
|
50
|
+
end
|
51
|
+
|
52
|
+
it "has a name" do
|
53
|
+
query = Oculus::Query.new(:name => 'foo')
|
54
|
+
query.name.should == 'foo'
|
55
|
+
end
|
56
|
+
|
57
|
+
it "has an author" do
|
58
|
+
query = Oculus::Query.new(:author => 'Paul')
|
59
|
+
query.author.should == 'Paul'
|
60
|
+
end
|
61
|
+
|
62
|
+
it "stores new queries in the data store on creation" do
|
63
|
+
Oculus.data_store.should_receive(:save_query)
|
64
|
+
query = Oculus::Query.create(:results => [['id', 'name'], [1, 'Paul']])
|
65
|
+
end
|
66
|
+
|
67
|
+
it "stores new queries in the data store on save" do
|
68
|
+
Oculus.data_store.should_receive(:save_query)
|
69
|
+
query = Oculus::Query.new(:results => [['id', 'name'], [1, 'Paul']])
|
70
|
+
query.save
|
71
|
+
end
|
72
|
+
|
73
|
+
it "retrieves cached queries from the data store" do
|
74
|
+
Oculus.data_store.should_receive(:load_query).with(1)
|
75
|
+
Oculus::Query.find(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "is not complete when no results are present" do
|
79
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
80
|
+
query.should_not be_complete
|
81
|
+
end
|
82
|
+
|
83
|
+
it "is complete when results are present" do
|
84
|
+
query = Oculus::Query.new(:results => [['id', 'name'], [1, 'Paul']])
|
85
|
+
query.should be_complete
|
86
|
+
end
|
87
|
+
|
88
|
+
it "is complete when there is an error" do
|
89
|
+
query = Oculus::Query.new(:error => "That's not how to write SQL")
|
90
|
+
query.should be_complete
|
91
|
+
end
|
92
|
+
|
93
|
+
it "is not successful when it's not complete" do
|
94
|
+
query = Oculus::Query.new(:query => 'SELECT * FROM users')
|
95
|
+
query.succeeded?.should be false
|
96
|
+
end
|
97
|
+
|
98
|
+
it "is successful when results are present" do
|
99
|
+
query = Oculus::Query.new(:results => [['id', 'name'], [1, 'Paul']])
|
100
|
+
query.succeeded?.should be true
|
101
|
+
end
|
102
|
+
|
103
|
+
it "is not successful when there is an error" do
|
104
|
+
query = Oculus::Query.new(:error => "That's not how to write SQL")
|
105
|
+
query.succeeded?.should be false
|
106
|
+
end
|
107
|
+
end
|