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
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Paul Rosania
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Oculus
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
[](http://travis-ci.org/paulrosania/oculus)
|
6
|
+
[](https://gemnasium.com/paulrosania/oculus)
|
7
|
+
|
8
|
+
Oculus is a web-based logging SQL client. It keeps a history of your queries
|
9
|
+
and the results they returned, so your research is always at hand, easy to share
|
10
|
+
and easy to repeat or reproduce in the future.
|
11
|
+
|
12
|
+
**Oculus will not prevent you from doing stupid things! I recommend using a
|
13
|
+
readonly MySQL account.**
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
$ gem install oculus
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Oculus is a Sinatra app. Run it from the command line, or mount `Oculus::Server`
|
22
|
+
as middleware in your Rack application.
|
23
|
+
|
24
|
+
For details on command line options, run:
|
25
|
+
|
26
|
+
oculus --help
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
1. Fork it
|
31
|
+
2. Make your changes
|
32
|
+
3. Send me a pull request
|
33
|
+
|
34
|
+
If you're making a big change, please open an Issue first, so we can discuss.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'cucumber/rake/task'
|
6
|
+
|
7
|
+
desc 'Run RSpec tests'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
9
|
+
task.rspec_opts = %w[--color --format documentation]
|
10
|
+
task.pattern = 'spec/*_spec.rb'
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Run Cucumber features'
|
14
|
+
Cucumber::Rake::Task.new(:cucumber) do |task|
|
15
|
+
task.cucumber_opts = 'features --format pretty'
|
16
|
+
end
|
17
|
+
|
18
|
+
task :default => [:spec, :cucumber]
|
data/TODO.md
ADDED
data/bin/oculus
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
begin
|
5
|
+
require 'vegas'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'vegas'
|
9
|
+
end
|
10
|
+
require 'oculus/server'
|
11
|
+
|
12
|
+
|
13
|
+
Vegas::Runner.new(Oculus::Server, 'oculus') do |runner, opts, app|
|
14
|
+
opts.on("-h", "--host HOST", "Database server hostname") do |host|
|
15
|
+
Oculus.connection_options[:host] = host
|
16
|
+
end
|
17
|
+
opts.on("-P", "--port PORT", "Database server port") do |port|
|
18
|
+
Oculus.connection_options[:port] = port.to_i
|
19
|
+
end
|
20
|
+
opts.on("-u", "--username USER", "Database username") do |username|
|
21
|
+
Oculus.connection_options[:username] = username
|
22
|
+
end
|
23
|
+
opts.on("-p", "--password PASSWORD", "Database password") do |password|
|
24
|
+
Oculus.connection_options[:password] = password
|
25
|
+
end
|
26
|
+
opts.on("-D", "--database DATABASE", "Database to use") do |db|
|
27
|
+
Oculus.connection_options[:database] = db
|
28
|
+
end
|
29
|
+
opts.on("-d", "--data DIRECTORY", "Data cache path (default: tmp/data)") do |path|
|
30
|
+
Oculus.cache_path = path
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Feature: Users can query the database
|
2
|
+
|
3
|
+
@javascript
|
4
|
+
Scenario: Running a new query
|
5
|
+
When I execute "SELECT * FROM oculus_users"
|
6
|
+
Then I should see 3 rows of results
|
7
|
+
|
8
|
+
Scenario: Loading a cached query
|
9
|
+
Given a query is cached with results:
|
10
|
+
| id | users |
|
11
|
+
| 1 | Paul |
|
12
|
+
| 2 | Amy |
|
13
|
+
| 3 | Peter |
|
14
|
+
When I load the cached query
|
15
|
+
Then I should see 3 rows of results
|
16
|
+
|
17
|
+
@javascript
|
18
|
+
Scenario: Deleting a query
|
19
|
+
Given a query is cached with results:
|
20
|
+
| id | users |
|
21
|
+
| 1 | Paul |
|
22
|
+
| 2 | Amy |
|
23
|
+
| 3 | Peter |
|
24
|
+
And I am on the history page
|
25
|
+
When I click delete
|
26
|
+
Then I should not see any queries
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Given /^a query is cached with results:$/ do |results|
|
2
|
+
Oculus::Query.create(:name => "all users", :query => "SELECT * FROM oculus_users", :results => results.raw)
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^I am on the history page$/ do
|
6
|
+
visit '/history'
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^I execute "([^"]*)"$/ do |query|
|
10
|
+
visit '/'
|
11
|
+
find('.CodeMirror :first-child :first-child').native.send_keys(query)
|
12
|
+
click_button 'Run'
|
13
|
+
end
|
14
|
+
|
15
|
+
When /^I load the cached query$/ do
|
16
|
+
visit '/history'
|
17
|
+
click_link 'all users'
|
18
|
+
end
|
19
|
+
|
20
|
+
When /^I click delete$/ do
|
21
|
+
find('.delete').click
|
22
|
+
end
|
23
|
+
|
24
|
+
Then /^I should see (\d+) rows of results$/ do |result_count|
|
25
|
+
page.has_css?(".results", :visible => true)
|
26
|
+
within('.results') do
|
27
|
+
all('tr').length.should == result_count.to_i
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Then /^I should not see any queries$/ do
|
32
|
+
within('#history') do
|
33
|
+
all('li').length.should == 0
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
$: << File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
|
2
|
+
|
3
|
+
require 'oculus'
|
4
|
+
require 'oculus/server'
|
5
|
+
require 'capybara/cucumber'
|
6
|
+
|
7
|
+
Capybara.app = Oculus::Server
|
8
|
+
|
9
|
+
Oculus.cache_path = 'tmp/test_cache'
|
10
|
+
Oculus.connection_options = {
|
11
|
+
:host => 'localhost',
|
12
|
+
:username => 'root',
|
13
|
+
:database => 'test'
|
14
|
+
}
|
15
|
+
|
16
|
+
Before do
|
17
|
+
FileUtils.mkdir_p('tmp/test_cache')
|
18
|
+
end
|
19
|
+
|
20
|
+
After do
|
21
|
+
FileUtils.rm_r('tmp/test_cache')
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
|
3
|
+
module Oculus
|
4
|
+
module Connection
|
5
|
+
class Mysql2
|
6
|
+
def initialize(options = {})
|
7
|
+
@connection = ::Mysql2::Client.new(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(sql)
|
11
|
+
results = @connection.query(sql)
|
12
|
+
[results.fields] + results.map(&:values) if results
|
13
|
+
rescue ::Mysql2::Error => e
|
14
|
+
raise Connection::Error.new(e.message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def thread_id
|
18
|
+
@connection.thread_id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Oculus
|
4
|
+
module Presenters
|
5
|
+
class QueryPresenter < SimpleDelegator
|
6
|
+
def formatted_date
|
7
|
+
date.strftime("%Y-%m-%d %H:%M") if date
|
8
|
+
end
|
9
|
+
|
10
|
+
def status
|
11
|
+
if complete?
|
12
|
+
if error
|
13
|
+
"error"
|
14
|
+
else
|
15
|
+
"done"
|
16
|
+
end
|
17
|
+
else
|
18
|
+
"loading"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def description
|
23
|
+
if name && name != ""
|
24
|
+
name
|
25
|
+
else
|
26
|
+
query = self.query
|
27
|
+
if query && query.length > 100
|
28
|
+
"#{query[0..97]}..."
|
29
|
+
else
|
30
|
+
query
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def named?
|
36
|
+
!!name
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'oculus/presenters/query_presenter'
|
data/lib/oculus/query.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Oculus
|
2
|
+
class Query
|
3
|
+
attr_accessor :id
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :author
|
6
|
+
attr_accessor :query
|
7
|
+
attr_accessor :results
|
8
|
+
attr_accessor :error
|
9
|
+
attr_accessor :date
|
10
|
+
attr_accessor :thread_id
|
11
|
+
|
12
|
+
def initialize(attributes = {})
|
13
|
+
attributes.each do |attr, value|
|
14
|
+
send("#{attr}=", value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
attrs = {
|
20
|
+
:name => name,
|
21
|
+
:author => author,
|
22
|
+
:query => query,
|
23
|
+
:date => date,
|
24
|
+
:thread_id => thread_id
|
25
|
+
}
|
26
|
+
attrs[:error] = error if error
|
27
|
+
attrs
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(connection)
|
31
|
+
self.results = connection.execute(query)
|
32
|
+
rescue Connection::Error => e
|
33
|
+
self.error = e.message
|
34
|
+
end
|
35
|
+
|
36
|
+
def save
|
37
|
+
@date = Time.now
|
38
|
+
Oculus.data_store.save_query(self)
|
39
|
+
end
|
40
|
+
|
41
|
+
def complete?
|
42
|
+
!!error || (!results.nil? && !results.empty?)
|
43
|
+
end
|
44
|
+
|
45
|
+
def succeeded?
|
46
|
+
complete? && !error
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
def create(attributes)
|
51
|
+
query = new(attributes)
|
52
|
+
query.save
|
53
|
+
query
|
54
|
+
end
|
55
|
+
|
56
|
+
def find(id)
|
57
|
+
Oculus.data_store.load_query(id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|