oculus 0.5.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/.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
|